FEAT: NEW WORKFLOW ENGINE (#3160)

Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: Yeuoly <admin@srmxy.cn>
Co-authored-by: JzoNg <jzongcode@gmail.com>
Co-authored-by: StyleZhang <jasonapring2015@outlook.com>
Co-authored-by: jyong <jyong@dify.ai>
Co-authored-by: nite-knite <nkCoding@gmail.com>
Co-authored-by: jyong <718720800@qq.com>
This commit is contained in:
takatost
2024-04-08 18:51:46 +08:00
committed by GitHub
parent 2fb9850af5
commit 7753ba2d37
1161 changed files with 103836 additions and 10327 deletions

View File

@@ -115,10 +115,9 @@ const AudioBtn = ({
className='z-10'
>
<div
className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || 'rounded-md bg-white'}`}
style={{ boxShadow: !isAudition ? '0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06)' : '' }}
className={`box-border p-0.5 flex items-center justify-center cursor-pointer ${isAudition || '!p-0 rounded-md bg-white'}`}
onClick={togglePlayPause}>
<div className={`w-6 h-6 rounded-md ${!isAudition ? 'hover:bg-gray-200' : 'hover:bg-gray-50'} ${(isPlaying && !hasEnded) ? s.pauseIcon : s.playIcon}`}></div>
<div className={`w-6 h-6 rounded-md ${!isAudition ? 'w-4 h-4 hover:bg-gray-50' : 'hover:bg-gray-50'} ${(isPlaying && !hasEnded) ? s.pauseIcon : s.playIcon}`}></div>
</div>
</Tooltip>
</div>

View File

@@ -0,0 +1,22 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import cn from 'classnames'
import { Plus } from '../icons/src/vender/line/general'
type Props = {
className?: string
onClick: () => void
}
const AddButton: FC<Props> = ({
className,
onClick,
}) => {
return (
<div className={cn(className, 'p-1 rounded-md cursor-pointer hover:bg-gray-200 select-none')} onClick={onClick}>
<Plus className='w-4 h-4 text-gray-500' />
</div>
)
}
export default React.memo(AddButton)

View File

@@ -17,7 +17,7 @@ const BasicContent: FC<BasicContentProps> = ({
if (annotation?.logAnnotation)
return <Markdown content={annotation?.logAnnotation.content || ''} />
return <Markdown content={content} />
return <Markdown content={content} className={`${item.isError && '!text-[#F04438]'}`} />
}
export default memo(BasicContent)

View File

@@ -2,7 +2,7 @@ import type {
FC,
ReactNode,
} from 'react'
import { memo } from 'react'
import { memo, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import type {
ChatConfig,
@@ -13,7 +13,9 @@ import AgentContent from './agent-content'
import BasicContent from './basic-content'
import SuggestedQuestions from './suggested-questions'
import More from './more'
import WorkflowProcess from './workflow-process'
import { AnswerTriangle } from '@/app/components/base/icons/src/vender/solid/general'
import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
import LoadingAnim from '@/app/components/app/chat/loading-anim'
import Citation from '@/app/components/app/chat/citation'
import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
@@ -27,6 +29,8 @@ type AnswerProps = {
answerIcon?: ReactNode
responding?: boolean
allToolIcons?: Record<string, string | Emoji>
showPromptLog?: boolean
chatAnswerContainerInner?: string
}
const Answer: FC<AnswerProps> = ({
item,
@@ -36,6 +40,8 @@ const Answer: FC<AnswerProps> = ({
answerIcon,
responding,
allToolIcons,
showPromptLog,
chatAnswerContainerInner,
}) => {
const { t } = useTranslation()
const {
@@ -44,9 +50,33 @@ const Answer: FC<AnswerProps> = ({
agent_thoughts,
more,
annotation,
workflowProcess,
} = item
const hasAgentThoughts = !!agent_thoughts?.length
const [containerWidth, setContainerWidth] = useState(0)
const [contentWidth, setContentWidth] = useState(0)
const containerRef = useRef<HTMLDivElement>(null)
const contentRef = useRef<HTMLDivElement>(null)
const getContainerWidth = () => {
if (containerRef.current)
setContainerWidth(containerRef.current?.clientWidth + 16)
}
const getContentWidth = () => {
if (contentRef.current)
setContentWidth(contentRef.current?.clientWidth)
}
useEffect(() => {
getContainerWidth()
}, [])
useEffect(() => {
if (!responding)
getContentWidth()
}, [responding])
return (
<div className='flex mb-2 last:mb-0'>
<div className='shrink-0 relative w-10 h-10'>
@@ -65,19 +95,43 @@ const Answer: FC<AnswerProps> = ({
)
}
</div>
<div className='chat-answer-container grow w-0 group ml-4'>
<div className='relative pr-10'>
<div className='chat-answer-container grow w-0 ml-4' ref={containerRef}>
<div className={`group relative pr-10 ${chatAnswerContainerInner}`}>
<AnswerTriangle className='absolute -left-2 top-0 w-2 h-3 text-gray-100' />
<div className='group relative inline-block px-4 py-3 max-w-full bg-gray-100 rounded-b-2xl rounded-tr-2xl text-sm text-gray-900'>
<div
ref={contentRef}
className={`
relative inline-block px-4 py-3 max-w-full bg-gray-100 rounded-b-2xl rounded-tr-2xl text-sm text-gray-900
${workflowProcess && 'w-full'}
`}
>
{annotation?.id && (
<div
className='absolute -top-3.5 -right-3.5 box-border flex items-center justify-center h-7 w-7 p-0.5 rounded-lg bg-white cursor-pointer text-[#444CE7] shadow-md group-hover:hidden'
>
<div className='p-1 rounded-lg bg-[#EEF4FF] '>
<MessageFast className='w-4 h-4' />
</div>
</div>
)}
{
!responding && (
<Operation
hasWorkflowProcess={!!workflowProcess}
maxSize={containerWidth - contentWidth - 4}
contentWidth={contentWidth}
item={item}
question={question}
index={index}
showPromptLog={showPromptLog}
/>
)
}
{
workflowProcess && (
<WorkflowProcess data={workflowProcess} hideInfo />
)
}
{
responding && !content && !hasAgentThoughts && (
<div className='flex items-center justify-center w-6 h-5'>

View File

@@ -4,6 +4,7 @@ import {
useMemo,
useState,
} from 'react'
import cn from 'classnames'
import { useTranslation } from 'react-i18next'
import type { ChatItem } from '../../types'
import { useChatContext } from '../context'
@@ -17,16 +18,25 @@ import {
ThumbsUp,
} from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
import TooltipPlus from '@/app/components/base/tooltip-plus'
import Log from '@/app/components/app/chat/log'
type OperationProps = {
item: ChatItem
question: string
index: number
showPromptLog?: boolean
maxSize: number
contentWidth: number
hasWorkflowProcess: boolean
}
const Operation: FC<OperationProps> = ({
item,
question,
index,
showPromptLog,
maxSize,
contentWidth,
hasWorkflowProcess,
}) => {
const { t } = useTranslation()
const {
@@ -63,39 +73,134 @@ const Operation: FC<OperationProps> = ({
setLocalFeedback({ rating })
}
const operationWidth = useMemo(() => {
let width = 0
if (!isOpeningStatement)
width += 28
if (!isOpeningStatement && showPromptLog)
width += 102 + 8
if (!isOpeningStatement && config?.text_to_speech?.enabled)
width += 33
if (!isOpeningStatement && config?.supportAnnotation && config?.annotation_reply?.enabled)
width += 56 + 8
if (config?.supportFeedback && !localFeedback?.rating && onFeedback && !isOpeningStatement)
width += 60 + 8
if (config?.supportFeedback && localFeedback?.rating && onFeedback && !isOpeningStatement)
width += 28 + 8
return width
}, [isOpeningStatement, showPromptLog, config?.text_to_speech?.enabled, config?.supportAnnotation, config?.annotation_reply?.enabled, config?.supportFeedback, localFeedback?.rating, onFeedback])
const positionRight = useMemo(() => operationWidth < maxSize, [operationWidth, maxSize])
return (
<div className='absolute top-[-14px] right-[-14px] flex justify-end gap-1'>
{
!isOpeningStatement && (
<>
<div
className={cn(
'absolute flex justify-end gap-1',
hasWorkflowProcess && '-top-3.5 -right-3.5',
!positionRight && '-top-3.5 -right-3.5',
!hasWorkflowProcess && positionRight && '!top-[9px]',
)}
style={(!hasWorkflowProcess && positionRight) ? { left: contentWidth + 8 } : {}}
>
{!isOpeningStatement && (
<CopyBtn
value={content}
className='hidden group-hover:block'
/>
)
}
)}
{(!isOpeningStatement && config?.text_to_speech?.enabled) && (
<AudioBtn
value={content}
voice={config?.text_to_speech?.voice}
className='hidden group-hover:block'
/>
)}
{(!isOpeningStatement && config?.supportAnnotation && config.annotation_reply?.enabled) && (
<AnnotationCtrlBtn
appId={config?.appId || ''}
messageId={id}
annotationId={annotation?.id || ''}
className='hidden group-hover:block ml-1 shrink-0'
cached={hasAnnotation}
query={question}
answer={content}
onAdded={(id, authorName) => onAnnotationAdded?.(id, authorName, question, content, index)}
onEdit={() => setIsShowReplyModal(true)}
onRemoved={() => onAnnotationRemoved?.(index)}
/>
)}
{!isOpeningStatement && (showPromptLog || config?.text_to_speech?.enabled) && (
<div className='hidden group-hover:flex items-center w-max h-[28px] p-0.5 rounded-lg bg-white border-[0.5px] border-gray-100 shadow-md shrink-0'>
{showPromptLog && (
<Log logItem={item} />
)}
{(config?.text_to_speech?.enabled) && (
<>
<div className='mx-1 w-[1px] h-[14px] bg-gray-200'/>
<AudioBtn
value={content}
voice={config?.text_to_speech?.voice}
className='hidden group-hover:block'
/>
</>
)}
</div>
)}
{(!isOpeningStatement && config?.supportAnnotation && config.annotation_reply?.enabled) && (
<AnnotationCtrlBtn
appId={config?.appId || ''}
messageId={id}
annotationId={annotation?.id || ''}
className='hidden group-hover:block ml-1 shrink-0'
cached={hasAnnotation}
query={question}
answer={content}
onAdded={(id, authorName) => onAnnotationAdded?.(id, authorName, question, content, index)}
onEdit={() => setIsShowReplyModal(true)}
onRemoved={() => onAnnotationRemoved?.(index)}
/>
)}
{
!positionRight && annotation?.id && (
<div
className='relative box-border flex items-center justify-center h-7 w-7 p-0.5 rounded-lg bg-white cursor-pointer text-[#444CE7] shadow-md group-hover:hidden'
>
<div className='p-1 rounded-lg bg-[#EEF4FF] '>
<MessageFast className='w-4 h-4' />
</div>
</div>
)
}
{
config?.supportFeedback && !localFeedback?.rating && onFeedback && !isOpeningStatement && (
<div className='hidden group-hover:flex ml-1 shrink-0 items-center px-0.5 bg-white border-[0.5px] border-gray-100 shadow-md text-gray-500 rounded-lg'>
<TooltipPlus popupContent={t('appDebug.operation.agree')}>
<div
className='flex items-center justify-center mr-0.5 w-6 h-6 rounded-md hover:bg-black/5 hover:text-gray-800 cursor-pointer'
onClick={() => handleFeedback('like')}
>
<ThumbsUp className='w-4 h-4' />
</div>
</TooltipPlus>
<TooltipPlus popupContent={t('appDebug.operation.disagree')}>
<div
className='flex items-center justify-center w-6 h-6 rounded-md hover:bg-black/5 hover:text-gray-800 cursor-pointer'
onClick={() => handleFeedback('dislike')}
>
<ThumbsDown className='w-4 h-4' />
</div>
</TooltipPlus>
</div>
)
}
{
config?.supportFeedback && localFeedback?.rating && onFeedback && !isOpeningStatement && (
<TooltipPlus popupContent={localFeedback.rating === 'like' ? t('appDebug.operation.cancelAgree') : t('appDebug.operation.cancelDisagree')}>
<div
className={`
flex items-center justify-center w-7 h-7 rounded-[10px] border-[2px] border-white cursor-pointer
${localFeedback.rating === 'like' && 'bg-blue-50 text-blue-600'}
${localFeedback.rating === 'dislike' && 'bg-red-100 text-red-600'}
`}
onClick={() => handleFeedback(null)}
>
{
localFeedback.rating === 'like' && (
<ThumbsUp className='w-4 h-4' />
)
}
{
localFeedback.rating === 'dislike' && (
<ThumbsDown className='w-4 h-4' />
)
}
</div>
</TooltipPlus>
)
}
</div>
<EditReplyModal
isShow={isShowReplyModal}
onHide={() => setIsShowReplyModal(false)}
@@ -109,65 +214,7 @@ const Operation: FC<OperationProps> = ({
createdAt={annotation?.created_at}
onRemove={() => onAnnotationRemoved?.(index)}
/>
{
annotation?.id && (
<div
className='relative box-border flex items-center justify-center h-7 w-7 p-0.5 rounded-lg bg-white cursor-pointer text-[#444CE7] shadow-md'
>
<div className='p-1 rounded-lg bg-[#EEF4FF] '>
<MessageFast className='w-4 h-4' />
</div>
</div>
)
}
{
config?.supportFeedback && !localFeedback?.rating && onFeedback && !isOpeningStatement && (
<div className='hidden group-hover:flex ml-1 shrink-0 items-center px-0.5 bg-white border-[0.5px] border-gray-100 shadow-md text-gray-500 rounded-lg'>
<TooltipPlus popupContent={t('appDebug.operation.agree')}>
<div
className='flex items-center justify-center mr-0.5 w-6 h-6 rounded-md hover:bg-black/5 hover:text-gray-800 cursor-pointer'
onClick={() => handleFeedback('like')}
>
<ThumbsUp className='w-4 h-4' />
</div>
</TooltipPlus>
<TooltipPlus popupContent={t('appDebug.operation.disagree')}>
<div
className='flex items-center justify-center w-6 h-6 rounded-md hover:bg-black/5 hover:text-gray-800 cursor-pointer'
onClick={() => handleFeedback('dislike')}
>
<ThumbsDown className='w-4 h-4' />
</div>
</TooltipPlus>
</div>
)
}
{
config?.supportFeedback && localFeedback?.rating && onFeedback && !isOpeningStatement && (
<TooltipPlus popupContent={localFeedback.rating === 'like' ? t('appDebug.operation.cancelAgree') : t('appDebug.operation.cancelDisagree')}>
<div
className={`
flex items-center justify-center w-7 h-7 rounded-[10px] border-[2px] border-white cursor-pointer
${localFeedback.rating === 'like' && 'bg-blue-50 text-blue-600'}
${localFeedback.rating === 'dislike' && 'bg-red-100 text-red-600'}
`}
onClick={() => handleFeedback(null)}
>
{
localFeedback.rating === 'like' && (
<ThumbsUp className='w-4 h-4' />
)
}
{
localFeedback.rating === 'dislike' && (
<ThumbsDown className='w-4 h-4' />
)
}
</div>
</TooltipPlus>
)
}
</div>
</>
)
}

View File

@@ -0,0 +1,108 @@
import {
useEffect,
useMemo,
useState,
} from 'react'
import cn from 'classnames'
import { useTranslation } from 'react-i18next'
import type { WorkflowProcess } from '../../types'
import { CheckCircle } from '@/app/components/base/icons/src/vender/solid/general'
import { AlertCircle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
import { Loading02 } from '@/app/components/base/icons/src/vender/line/general'
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import NodePanel from '@/app/components/workflow/run/node'
type WorkflowProcessProps = {
data: WorkflowProcess
grayBg?: boolean
expand?: boolean
hideInfo?: boolean
}
const WorkflowProcessItem = ({
data,
grayBg,
expand = false,
hideInfo = false,
}: WorkflowProcessProps) => {
const { t } = useTranslation()
const [collapse, setCollapse] = useState(!expand)
const running = data.status === WorkflowRunningStatus.Running
const succeeded = data.status === WorkflowRunningStatus.Succeeded
const failed = data.status === WorkflowRunningStatus.Failed || data.status === WorkflowRunningStatus.Stopped
const background = useMemo(() => {
if (running && !collapse)
return 'linear-gradient(180deg, #E1E4EA 0%, #EAECF0 100%)'
if (succeeded && !collapse)
return 'linear-gradient(180deg, #ECFDF3 0%, #F6FEF9 100%)'
if (failed && !collapse)
return 'linear-gradient(180deg, #FEE4E2 0%, #FEF3F2 100%)'
}, [running, succeeded, failed, collapse])
useEffect(() => {
setCollapse(!expand)
}, [expand])
return (
<div
className={cn(
'mb-2 rounded-xl border-[0.5px] border-black/[0.08]',
collapse ? 'py-[7px]' : hideInfo ? 'pt-2 pb-1' : 'py-2',
collapse && (!grayBg ? 'bg-white' : 'bg-gray-50'),
hideInfo ? 'mx-[-8px] px-1' : 'w-full px-3',
)}
style={{
background,
}}
>
<div
className={cn(
'flex items-center h-[18px] cursor-pointer',
hideInfo && 'px-[6px]',
)}
onClick={() => setCollapse(!collapse)}
>
{
running && (
<Loading02 className='shrink-0 mr-1 w-3 h-3 text-[#667085] animate-spin' />
)
}
{
succeeded && (
<CheckCircle className='shrink-0 mr-1 w-3 h-3 text-[#12B76A]' />
)
}
{
failed && (
<AlertCircle className='shrink-0 mr-1 w-3 h-3 text-[#F04438]' />
)
}
<div className='grow text-xs font-medium text-gray-700'>
{t('workflow.common.workflowProcess')}
</div>
<ChevronRight className={`'ml-1 w-3 h-3 text-gray-500' ${collapse ? '' : 'rotate-90'}`} />
</div>
{
!collapse && (
<div className='mt-1.5'>
{
data.tracing.map(node => (
<div key={node.id} className='mb-1 last-of-type:mb-0'>
<NodePanel
nodeInfo={node}
hideInfo={hideInfo}
/>
</div>
))
}
</div>
)
}
</div>
)
}
export default WorkflowProcessItem

View File

@@ -19,6 +19,7 @@ import { useToastContext } from '@/app/components/base/toast'
import { ssePost } from '@/service/base'
import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel'
import type { Annotation } from '@/models/log'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
type GetAbortController = (abortController: AbortController) => void
type SendCallback = {
@@ -318,10 +319,21 @@ export const useChat = (
const requestion = draft[index - 1]
draft[index - 1] = {
...requestion,
log: newResponseItem.message,
}
draft[index] = {
...draft[index],
log: [
...newResponseItem.message,
...(newResponseItem.message[newResponseItem.message.length - 1].role !== 'assistant'
? [
{
role: 'assistant',
text: newResponseItem.answer,
files: newResponseItem.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [],
},
]
: []),
],
more: {
time: dayjs.unix(newResponseItem.created_at).format('hh:mm A'),
tokens: newResponseItem.answer_tokens + newResponseItem.message_tokens,
@@ -422,6 +434,52 @@ export const useChat = (
})
handleUpdateChatList(newChatList)
},
onWorkflowStarted: ({ workflow_run_id, task_id }) => {
taskIdRef.current = task_id
responseItem.workflow_run_id = workflow_run_id
responseItem.workflowProcess = {
status: WorkflowRunningStatus.Running,
tracing: [],
}
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
draft[currentIndex] = {
...draft[currentIndex],
...responseItem,
}
}))
},
onWorkflowFinished: ({ data }) => {
responseItem.workflowProcess!.status = data.status as WorkflowRunningStatus
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
draft[currentIndex] = {
...draft[currentIndex],
...responseItem,
}
}))
},
onNodeStarted: ({ data }) => {
responseItem.workflowProcess!.tracing!.push(data as any)
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
draft[currentIndex] = {
...draft[currentIndex],
...responseItem,
}
}))
},
onNodeFinished: ({ data }) => {
const currentIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.node_id === data.node_id)
responseItem.workflowProcess!.tracing[currentIndex] = data as any
handleUpdateChatList(produce(chatListRef.current, (draft) => {
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
draft[currentIndex] = {
...draft[currentIndex],
...responseItem,
}
}))
},
})
return true
}, [

View File

@@ -7,6 +7,7 @@ import {
useCallback,
useEffect,
useRef,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import { debounce } from 'lodash-es'
@@ -24,6 +25,8 @@ import { ChatContextProvider } from './context'
import type { Emoji } from '@/app/components/tools/types'
import Button from '@/app/components/base/button'
import { StopCircle } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
import PromptLogModal from '@/app/components/base/prompt-log-modal'
import { useStore as useAppStore } from '@/app/components/app/store'
export type ChatProps = {
chatList: ChatItem[]
@@ -47,6 +50,7 @@ export type ChatProps = {
onAnnotationRemoved?: (index: number) => void
chatNode?: ReactNode
onFeedback?: (messageId: string, feedback: Feedback) => void
chatAnswerContainerInner?: string
}
const Chat: FC<ChatProps> = ({
config,
@@ -70,8 +74,11 @@ const Chat: FC<ChatProps> = ({
onAnnotationRemoved,
chatNode,
onFeedback,
chatAnswerContainerInner,
}) => {
const { t } = useTranslation()
const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal } = useAppStore()
const [width, setWidth] = useState(0)
const chatContainerRef = useRef<HTMLDivElement>(null)
const chatContainerInnerRef = useRef<HTMLDivElement>(null)
const chatFooterRef = useRef<HTMLDivElement>(null)
@@ -84,6 +91,9 @@ const Chat: FC<ChatProps> = ({
}, [])
const handleWindowResize = useCallback(() => {
if (chatContainerRef.current)
setWidth(document.body.clientWidth - (chatContainerRef.current?.clientWidth + 16) - 8)
if (chatContainerRef.current && chatFooterRef.current)
chatFooterRef.current.style.width = `${chatContainerRef.current.clientWidth}px`
@@ -182,6 +192,8 @@ const Chat: FC<ChatProps> = ({
answerIcon={answerIcon}
responding={isLast && isResponding}
allToolIcons={allToolIcons}
showPromptLog={showPromptLog}
chatAnswerContainerInner={chatAnswerContainerInner}
/>
)
}
@@ -189,9 +201,7 @@ const Chat: FC<ChatProps> = ({
<Question
key={item.id}
item={item}
showPromptLog={showPromptLog}
questionIcon={questionIcon}
isResponding={isResponding}
/>
)
})
@@ -238,6 +248,16 @@ const Chat: FC<ChatProps> = ({
}
</div>
</div>
{showPromptLogModal && (
<PromptLogModal
width={width}
currentLogItem={currentLogItem}
onCancel={() => {
setCurrentLogItem()
setShowPromptLogModal(false)
}}
/>
)}
</div>
</ChatContextProvider>
)

View File

@@ -4,28 +4,21 @@ import type {
} from 'react'
import {
memo,
useRef,
} from 'react'
import type { ChatItem } from '../types'
import { QuestionTriangle } from '@/app/components/base/icons/src/vender/solid/general'
import { User } from '@/app/components/base/icons/src/public/avatar'
import Log from '@/app/components/app/chat/log'
import { Markdown } from '@/app/components/base/markdown'
import ImageGallery from '@/app/components/base/image-gallery'
type QuestionProps = {
item: ChatItem
showPromptLog?: boolean
questionIcon?: ReactNode
isResponding?: boolean
}
const Question: FC<QuestionProps> = ({
item,
showPromptLog,
isResponding,
questionIcon,
}) => {
const ref = useRef(null)
const {
content,
message_files,
@@ -34,14 +27,9 @@ const Question: FC<QuestionProps> = ({
const imgSrcs = message_files?.length ? message_files.map(item => item.url) : []
return (
<div className='flex justify-end mb-2 last:mb-0 pl-10' ref={ref}>
<div className='flex justify-end mb-2 last:mb-0 pl-10'>
<div className='group relative mr-4'>
<QuestionTriangle className='absolute -right-2 top-0 w-2 h-3 text-[#D1E9FF]/50' />
{
showPromptLog && !isResponding && (
<Log log={item.log!} containerRef={ref} />
)
}
<div className='px-4 py-3 bg-[#D1E9FF]/50 rounded-b-2xl rounded-tl-2xl text-sm text-gray-900'>
{
!!imgSrcs.length && (

View File

@@ -4,6 +4,8 @@ import type {
VisionSettings,
} from '@/types/app'
import type { IChatItem } from '@/app/components/app/chat/type'
import type { NodeTracing } from '@/types/workflow'
import type { WorkflowRunningStatus } from '@/app/components/workflow/types'
export type { VisionFile } from '@/types/app'
export { TransferMethod } from '@/types/app'
@@ -48,7 +50,16 @@ export type ChatConfig = Omit<ModelConfig, 'model'> & {
supportCitationHitInfo?: boolean
}
export type ChatItem = IChatItem
export type WorkflowProcess = {
status: WorkflowRunningStatus
tracing: NodeTracing[]
expand?: boolean // for UI
}
export type ChatItem = IChatItem & {
isError?: boolean
workflowProcess?: WorkflowProcess
}
export type OnSend = (message: string, files?: VisionFile[]) => void

View File

@@ -2,6 +2,7 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Button from '../button'
export type IConfirmUIProps = {
type: 'info' | 'warning'
@@ -41,8 +42,8 @@ const ConfirmUI: FC<IConfirmUIProps> = ({
</div>
<div className='flex gap-3 mt-4 ml-12'>
<div onClick={onConfirm} className='w-20 leading-9 text-center text-white border rounded-lg cursor-pointer h-9 border-color-primary-700 bg-primary-700'>{confirmText || t('common.operation.confirm')}</div>
<div onClick={onCancel} className='w-20 leading-9 text-center text-gray-500 border rounded-lg cursor-pointer h-9 border-color-gray-200'>{cancelText || t('common.operation.cancel')}</div>
<Button type='primary' onClick={onConfirm} className='flex items-center justify-center w-20 text-center text-white rounded-lg cursor-pointer h-9 '>{confirmText || t('common.operation.confirm')}</Button>
<Button onClick={onCancel} className='flex items-center justify-center w-20 text-center text-gray-500 border rounded-lg cursor-pointer h-9 border-color-gray-200'>{cancelText || t('common.operation.cancel')}</Button>
</div>
</div>

View File

@@ -47,7 +47,7 @@ export default function Drawer({
<Dialog.Overlay
className={`z-40 fixed inset-0 ${!mask ? '' : 'bg-black bg-opacity-30'}`}
/>
<div className={`z-50 flex flex-col justify-between bg-white w-full
<div className={`relative z-50 flex flex-col justify-between bg-white w-full
max-w-sm p-6 overflow-hidden text-left align-middle
shadow-xl ${panelClassname}`}>
<>

View File

@@ -0,0 +1,27 @@
import {
createContext,
useRef,
} from 'react'
import type {
FeaturesState,
FeaturesStore,
} from './store'
import { createFeaturesStore } from './store'
export const FeaturesContext = createContext<FeaturesStore | null>(null)
type FeaturesProviderProps = {
children: React.ReactNode
} & Partial<FeaturesState>
export const FeaturesProvider = ({ children, ...props }: FeaturesProviderProps) => {
const storeRef = useRef<FeaturesStore>()
if (!storeRef.current)
storeRef.current = createFeaturesStore(props)
return (
<FeaturesContext.Provider value={storeRef.current}>
{children}
</FeaturesContext.Provider>
)
}

View File

@@ -0,0 +1,31 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import GroupName from '@/app/components/app/configuration/base/group-name'
export type IFeatureGroupProps = {
title: string
description?: string
children: React.ReactNode
}
const FeatureGroup: FC<IFeatureGroupProps> = ({
title,
description,
children,
}) => {
return (
<div className='mb-6'>
<div className='mb-2'>
<GroupName name={title} />
{description && (
<div className='text-xs font-normal text-gray-500'>{description}</div>
)}
</div>
<div className='space-y-2'>
{children}
</div>
</div>
)
}
export default React.memo(FeatureGroup)

View File

@@ -0,0 +1,96 @@
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import produce from 'immer'
import cn from 'classnames'
import s from './style.module.css'
import Switch from '@/app/components/base/switch'
import { FeatureEnum } from '@/app/components/base/features/types'
import { useFeaturesStore } from '@/app/components/base/features/hooks'
import { useModalContext } from '@/context/modal-context'
export type IFeatureItemProps = {
icon: React.ReactNode
previewImgClassName?: string
title: string
description: string
value: boolean
onChange: (type: FeatureEnum, value: boolean) => void
type: FeatureEnum
}
const FeatureItem: FC<IFeatureItemProps> = ({
icon,
previewImgClassName,
title,
description,
value,
onChange,
type,
}) => {
const featuresStore = useFeaturesStore()
const { setShowModerationSettingModal } = useModalContext()
const handleChange = useCallback((newValue: boolean) => {
const {
features,
setFeatures,
} = featuresStore!.getState()
if (newValue && !features.moderation?.type && type === FeatureEnum.moderation) {
setShowModerationSettingModal({
payload: {
enabled: true,
type: 'keywords',
config: {
keywords: '',
inputs_config: {
enabled: true,
preset_response: '',
},
},
},
onSaveCallback: (newModeration) => {
setFeatures(produce(features, (draft) => {
draft.moderation = newModeration
}))
},
onCancelCallback: () => {
setFeatures(produce(features, (draft) => {
draft.moderation = { enabled: false }
}))
},
})
return
}
onChange(type, newValue)
}, [type, onChange, featuresStore, setShowModerationSettingModal])
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'>
{/* icon */}
<div
className='shrink-0 flex items-center justify-center w-8 h-8 rounded-lg border border-gray-200 bg-white'
style={{
boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)',
}}
>
{icon}
</div>
<div>
<div className='text-sm font-semibold text-gray-800'>{title}</div>
<div className='text-xs font-normal text-gray-500'>{description}</div>
</div>
</div>
<Switch onChange={handleChange} defaultValue={value} />
{
previewImgClassName && (
<div className={cn(s.preview, s[previewImgClassName])}>
</div>)
}
</div>
)
}
export default React.memo(FeatureItem)

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 84 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -0,0 +1,39 @@
.preview {
display: none;
position: absolute;
transform: translate(480px, -54px);
width: 280px;
height: 360px;
background: center center no-repeat;
background-size: contain;
border-radius: 8px;
}
.wrap:hover .preview {
display: block;
}
.openingStatementPreview {
background-image: url(./preview-imgs/opening-statement.png);
}
.suggestedQuestionsAfterAnswerPreview {
background-image: url(./preview-imgs/suggested-questions-after-answer.svg);
}
.moreLikeThisPreview {
background-image: url(./preview-imgs/more-like-this.svg);
}
.speechToTextPreview {
background-image: url(./preview-imgs/speech-to-text.svg);
}
.textToSpeechPreview {
@apply shadow-lg rounded-lg;
background-image: url(./preview-imgs/text-to-audio-preview-assistant@2x.png);
}
.citationPreview {
background-image: url(./preview-imgs/citation.svg);
}

View File

@@ -0,0 +1,147 @@
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import produce from 'immer'
import { useTranslation } from 'react-i18next'
import {
useFeatures,
useFeaturesStore,
} from '../hooks'
import type { OnFeaturesChange } from '../types'
import FeatureGroup from './feature-group'
import FeatureItem from './feature-item'
import Modal from '@/app/components/base/modal'
import SuggestedQuestionsAfterAnswerIcon from '@/app/components/app/configuration/base/icons/suggested-questions-after-answer-icon'
import { Microphone01, Speaker } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
import { Citations } from '@/app/components/base/icons/src/vender/solid/editor'
import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files'
import { MessageHeartCircle } from '@/app/components/base/icons/src/vender/solid/communication'
import { FeatureEnum } from '@/app/components/base/features/types'
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
export type FeatureModalProps = {
onChange?: OnFeaturesChange
}
const FeatureModal: FC<FeatureModalProps> = ({
onChange,
}) => {
const { t } = useTranslation()
const { data: speech2textDefaultModel } = useDefaultModel(ModelTypeEnum.speech2text)
const { data: text2speechDefaultModel } = useDefaultModel(ModelTypeEnum.tts)
const featuresStore = useFeaturesStore()
const setShowFeaturesModal = useFeatures(s => s.setShowFeaturesModal)
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] = {
...draft[type],
enabled,
}
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
}, [featuresStore, onChange])
return (
<Modal
isShow
onClose={handleCancelModal}
className='w-[400px]'
title={t('appDebug.operation.addFeature')}
closable
overflowVisible
>
<div className='pt-5 pb-10'>
{/* Chat Feature */}
<FeatureGroup
title={t('appDebug.feature.groupChat.title')}
description={t('appDebug.feature.groupChat.description') as string}
>
<>
<FeatureItem
icon={<MessageHeartCircle className='w-4 h-4 text-[#DD2590]' />}
previewImgClassName='openingStatementPreview'
title={t('appDebug.feature.conversationOpener.title')}
description={t('appDebug.feature.conversationOpener.description')}
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={!!features.suggested?.enabled}
onChange={handleChange}
type={FeatureEnum.suggested}
/>
{
!!text2speechDefaultModel && (
<FeatureItem
icon={<Speaker className='w-4 h-4 text-[#7839EE]' />}
previewImgClassName='textToSpeechPreview'
title={t('appDebug.feature.textToSpeech.title')}
description={t('appDebug.feature.textToSpeech.description')}
value={!!features.text2speech?.enabled}
onChange={handleChange}
type={FeatureEnum.text2speech}
/>
)
}
{
!!speech2textDefaultModel && (
<FeatureItem
icon={<Microphone01 className='w-4 h-4 text-[#7839EE]' />}
previewImgClassName='speechToTextPreview'
title={t('appDebug.feature.speechToText.title')}
description={t('appDebug.feature.speechToText.description')}
value={!!features.speech2text?.enabled}
onChange={handleChange}
type={FeatureEnum.speech2text}
/>
)
}
<FeatureItem
icon={<Citations className='w-4 h-4 text-[#FD853A]' />}
previewImgClassName='citationPreview'
title={t('appDebug.feature.citation.title')}
description={t('appDebug.feature.citation.description')}
value={!!features.citation?.enabled}
onChange={handleChange}
type={FeatureEnum.citation}
/>
</>
</FeatureGroup>
<FeatureGroup title={t('appDebug.feature.toolbox.title')}>
<>
<FeatureItem
icon={<FileSearch02 className='w-4 h-4 text-[#039855]' />}
previewImgClassName=''
title={t('appDebug.feature.moderation.title')}
description={t('appDebug.feature.moderation.description')}
value={!!features.moderation?.enabled}
onChange={handleChange}
type={FeatureEnum.moderation}
/>
</>
</FeatureGroup>
</div>
</Modal>
)
}
export default React.memo(FeatureModal)

View File

@@ -0,0 +1,41 @@
'use client'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { useFeatures } from '../hooks'
import type { OnFeaturesChange } from '../types'
import FeatureModal from './feature-modal'
import Button from '@/app/components/base/button'
import { Plus } from '@/app/components/base/icons/src/vender/line/general'
type ChooseFeatureProps = {
onChange?: OnFeaturesChange
disabled?: boolean
}
const ChooseFeature = ({
onChange,
disabled,
}: ChooseFeatureProps) => {
const { t } = useTranslation()
const showFeaturesModal = useFeatures(s => s.showFeaturesModal)
const setShowFeaturesModal = useFeatures(s => s.setShowFeaturesModal)
return (
<>
<Button
className={`
px-3 py-0 h-8 rounded-lg border border-primary-100 bg-primary-25 shadow-xs text-xs font-semibold text-primary-600
${disabled && 'cursor-not-allowed opacity-50'}
`}
onClick={() => !disabled && setShowFeaturesModal(true)}
>
<Plus className='mr-1 w-4 h-4' />
{t('appDebug.operation.addFeature')}
</Button>
{
showFeaturesModal && (
<FeatureModal onChange={onChange} />
)
}
</>
)
}
export default React.memo(ChooseFeature)

View File

@@ -0,0 +1,25 @@
'use client'
import React, { type FC } from 'react'
import { useTranslation } from 'react-i18next'
import Panel from '@/app/components/app/configuration/base/feature-panel'
import { Citations } from '@/app/components/base/icons/src/vender/solid/editor'
const Citation: FC = () => {
const { t } = useTranslation()
return (
<Panel
title={
<div className='flex items-center gap-2'>
<div>{t('appDebug.feature.citation.title')}</div>
</div>
}
headerIcon={<Citations className='w-4 h-4 text-[#FD853A]' />}
headerRight={
<div className='text-xs text-gray-500'>{t('appDebug.feature.citation.resDes')}</div>
}
noBodySpacing
/>
)
}
export default React.memo(Citation)

View File

@@ -0,0 +1,63 @@
'use client'
import produce from 'immer'
import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import type { OnFeaturesChange } from '../../types'
import {
useFeatures,
useFeaturesStore,
} from '../../hooks'
import ParamConfig from './param-config'
import Switch from '@/app/components/base/switch'
import { File05 } from '@/app/components/base/icons/src/vender/solid/files'
type FileUploadProps = {
onChange?: OnFeaturesChange
disabled?: boolean
}
const FileUpload = ({
onChange,
disabled,
}: FileUploadProps) => {
const { t } = useTranslation()
const featuresStore = useFeaturesStore()
const file = useFeatures(s => s.features.file)
const handleSwitch = useCallback((value: boolean) => {
const {
features,
setFeatures,
} = featuresStore!.getState()
const newFeatures = produce(features, (draft) => {
if (draft.file?.image)
draft.file.image.enabled = value
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
}, [featuresStore, onChange])
return (
<div className='flex items-center px-3 h-12 bg-gray-50 rounded-xl overflow-hidden'>
<div className='shrink-0 flex items-center justify-center mr-1 w-6 h-6'>
<File05 className='shrink-0 w-4 h-4 text-[#6938EF]' />
</div>
<div className='shrink-0 mr-2 whitespace-nowrap text-sm text-gray-800 font-semibold'>
{t('common.imageUploader.imageUpload')}
</div>
<div className='grow' />
<div className='flex items-center'>
<ParamConfig onChange={onChange} disabled={disabled} />
<div className='ml-4 mr-3 w-[1px] h-3.5 bg-gray-200'></div>
<Switch
defaultValue={file?.image?.enabled}
onChange={handleSwitch}
disabled={disabled}
size='md'
/>
</div>
</div>
)
}
export default React.memo(FileUpload)

View File

@@ -0,0 +1,119 @@
'use client'
import produce from 'immer'
import React, { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import type { OnFeaturesChange } from '../../types'
import {
useFeatures,
useFeaturesStore,
} from '../../hooks'
import RadioGroup from './radio-group'
import { TransferMethod } from '@/types/app'
import ParamItem from '@/app/components/base/param-item'
const MIN = 1
const MAX = 6
type ParamConfigContentProps = {
onChange?: OnFeaturesChange
}
const ParamConfigContent = ({
onChange,
}: ParamConfigContentProps) => {
const { t } = useTranslation()
const featuresStore = useFeaturesStore()
const file = useFeatures(s => s.features.file)
const transferMethod = useMemo(() => {
if (!file?.image?.transfer_methods || file?.image.transfer_methods.length === 2)
return TransferMethod.all
return file.image.transfer_methods[0]
}, [file?.image?.transfer_methods])
const handleTransferMethodsChange = useCallback((value: TransferMethod) => {
const {
features,
setFeatures,
} = featuresStore!.getState()
const newFeatures = produce(features, (draft) => {
if (draft.file?.image) {
if (TransferMethod.all)
draft.file.image.transfer_methods = [TransferMethod.remote_url, TransferMethod.local_file]
else
draft.file.image.transfer_methods = [value]
}
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
}, [featuresStore, onChange])
const handleLimitsChange = useCallback((_key: string, value: number) => {
if (!value)
return
const {
features,
setFeatures,
} = featuresStore!.getState()
const newFeatures = produce(features, (draft) => {
if (draft.file?.image)
draft.file.image.number_limits = value
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
}, [featuresStore, onChange])
return (
<div>
<div>
<div className='leading-6 text-base font-semibold text-gray-800'>{t('common.operation.settings')}</div>
<div className='pt-3 space-y-6'>
<div>
<div className='mb-2 leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.vision.visionSettings.uploadMethod')}</div>
<RadioGroup
className='space-x-3'
options={[
{
label: t('appDebug.vision.visionSettings.both'),
value: TransferMethod.all,
},
{
label: t('appDebug.vision.visionSettings.localUpload'),
value: TransferMethod.local_file,
},
{
label: t('appDebug.vision.visionSettings.url'),
value: TransferMethod.remote_url,
},
]}
value={transferMethod}
onChange={handleTransferMethodsChange}
/>
</div>
<div>
<ParamItem
id='upload_limit'
className=''
name={t('appDebug.vision.visionSettings.uploadLimit')}
noTooltip
{...{
default: 2,
step: 1,
min: MIN,
max: MAX,
}}
value={file?.image?.number_limits || 3}
enable={true}
onChange={handleLimitsChange}
/>
</div>
</div>
</div>
</div>
)
}
export default React.memo(ParamConfigContent)

View File

@@ -0,0 +1,49 @@
'use client'
import { memo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
import type { OnFeaturesChange } from '../../types'
import ParamConfigContent from './param-config-content'
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
type ParamsConfigProps = {
onChange?: OnFeaturesChange
disabled?: boolean
}
const ParamsConfig = ({
onChange,
disabled,
}: ParamsConfigProps) => {
const { t } = useTranslation()
const [open, setOpen] = useState(false)
return (
<PortalToFollowElem
open={open}
onOpenChange={setOpen}
placement='bottom-end'
offset={{
mainAxis: 4,
}}
>
<PortalToFollowElemTrigger onClick={() => !disabled && setOpen(v => !v)}>
<div className={cn('flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200', disabled && 'cursor-not-allowed opacity-50')}>
<Settings01 className='w-3.5 h-3.5 ' />
<div className='ml-1 leading-[18px] text-xs font-medium '>{t('appDebug.voice.settings')}</div>
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent style={{ zIndex: 50 }}>
<div className='w-80 sm:w-[412px] p-4 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg space-y-3'>
<ParamConfigContent onChange={onChange} />
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default memo(ParamsConfig)

View File

@@ -0,0 +1,40 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import cn from 'classnames'
import s from './style.module.css'
type OPTION = {
label: string
value: any
}
type Props = {
className?: string
options: OPTION[]
value: any
onChange: (value: any) => void
}
const RadioGroup: FC<Props> = ({
className = '',
options,
value,
onChange,
}) => {
return (
<div className={cn(className, 'flex')}>
{options.map(item => (
<div
key={item.value}
className={cn(s.item, item.value === value && s.checked)}
onClick={() => onChange(item.value)}
>
<div className={s.radio}></div>
<div className='text-[13px] font-medium text-gray-900'>{item.label}</div>
</div>
))}
</div>
)
}
export default React.memo(RadioGroup)

View File

@@ -0,0 +1,24 @@
.item {
@apply grow flex items-center h-8 px-2.5 rounded-lg bg-gray-25 border border-gray-100 cursor-pointer space-x-2;
}
.item:hover {
background-color: #ffffff;
border-color: #B2CCFF;
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
}
.item.checked {
background-color: #ffffff;
border-color: #528BFF;
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.06), 0px 1px 3px 0px rgba(16, 24, 40, 0.10);
}
.radio {
@apply w-4 h-4 border-[2px] border-gray-200 rounded-full;
}
.item.checked .radio {
border-width: 5px;
border-color: #155eef;
}

View File

@@ -0,0 +1,115 @@
import {
memo,
useMemo,
} from 'react'
import { useTranslation } from 'react-i18next'
import type { OnFeaturesChange } from '../types'
import { useFeatures } from '../hooks'
import FileUpload from './file-upload'
import OpeningStatement from './opening-statement'
import type { OpeningStatementProps } from './opening-statement'
import SuggestedQuestionsAfterAnswer from './suggested-questions-after-answer'
import TextToSpeech from './text-to-speech'
import SpeechToText from './speech-to-text'
import Citation from './citation'
import Moderation from './moderation'
export type FeaturePanelProps = {
onChange?: OnFeaturesChange
openingStatementProps: OpeningStatementProps
disabled?: boolean
}
const FeaturePanel = ({
onChange,
openingStatementProps,
disabled,
}: FeaturePanelProps) => {
const { t } = useTranslation()
const features = useFeatures(s => s.features)
const showAdvanceFeature = useMemo(() => {
return features.opening?.enabled || features.suggested?.enabled || features.speech2text?.enabled || features.text2speech?.enabled || features.citation?.enabled
}, [features])
const showToolFeature = useMemo(() => {
return features.moderation?.enabled
}, [features])
return (
<div className='space-y-3'>
<FileUpload
onChange={onChange}
disabled={disabled}
/>
{
showAdvanceFeature && (
<div>
<div className='flex items-center'>
<div className='shrink-0 text-xs font-semibold text-gray-500'>
{t('appDebug.feature.groupChat.title')}
</div>
<div
className='grow ml-3 h-[1px]'
style={{ background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, #F3F4F6 100%)' }}
></div>
</div>
<div className='py-2 space-y-2'>
{
features.opening?.enabled && (
<OpeningStatement
{...openingStatementProps}
onChange={onChange}
readonly={disabled}
/>
)
}
{
features.suggested?.enabled && (
<SuggestedQuestionsAfterAnswer />
)
}
{
features.text2speech?.enabled && (
<TextToSpeech onChange={onChange} disabled={disabled} />
)
}
{
features.speech2text?.enabled && (
<SpeechToText />
)
}
{
features.citation?.enabled && (
<Citation />
)
}
</div>
</div>
)
}
{
showToolFeature && (
<div>
<div className='flex items-center'>
<div className='shrink-0 text-xs font-semibold text-gray-500'>
{t('appDebug.feature.groupChat.title')}
</div>
<div
className='grow ml-3 h-[1px]'
style={{ background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, #F3F4F6 100%)' }}
></div>
</div>
<div className='py-2 space-y-2'>
{
features.moderation?.enabled && (
<Moderation onChange={onChange} disabled={disabled} />
)
}
</div>
</div>
)
}
</div>
)
}
export default memo(FeaturePanel)

View File

@@ -0,0 +1,80 @@
import type { FC } from 'react'
import { memo } from 'react'
import { useContext } from 'use-context-selector'
import type { CodeBasedExtensionForm } from '@/models/common'
import I18n from '@/context/i18n'
import { PortalSelect } from '@/app/components/base/select'
import type { ModerationConfig } from '@/models/debug'
type FormGenerationProps = {
forms: CodeBasedExtensionForm[]
value: ModerationConfig['config']
onChange: (v: Record<string, string>) => void
}
const FormGeneration: FC<FormGenerationProps> = ({
forms,
value,
onChange,
}) => {
const { locale } = useContext(I18n)
const handleFormChange = (type: string, v: string) => {
onChange({ ...value, [type]: v })
}
return (
<>
{
forms.map((form, index) => (
<div
key={index}
className='py-2'
>
<div className='flex items-center h-9 text-sm font-medium text-gray-900'>
{locale === 'zh-Hans' ? form.label['zh-Hans'] : form.label['en-US']}
</div>
{
form.type === 'text-input' && (
<input
value={value?.[form.variable] || ''}
className='block px-3 w-full h-9 bg-gray-100 rounded-lg text-sm text-gray-900 outline-none appearance-none'
placeholder={form.placeholder}
onChange={e => handleFormChange(form.variable, e.target.value)}
/>
)
}
{
form.type === 'paragraph' && (
<div className='relative px-3 py-2 h-[88px] bg-gray-100 rounded-lg'>
<textarea
value={value?.[form.variable] || ''}
className='block w-full h-full bg-transparent text-sm outline-none appearance-none resize-none'
placeholder={form.placeholder}
onChange={e => handleFormChange(form.variable, e.target.value)}
/>
</div>
)
}
{
form.type === 'select' && (
<PortalSelect
value={value?.[form.variable]}
items={form.options.map((option) => {
return {
name: option.label[locale === 'zh-Hans' ? 'zh-Hans' : 'en-US'],
value: option.value,
}
})}
onSelect={item => handleFormChange(form.variable, item.value as string)}
popupClassName='w-[576px] !z-[102]'
/>
)
}
</div>
))
}
</>
)
}
export default memo(FormGeneration)

View File

@@ -0,0 +1,108 @@
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,
useFeaturesStore,
} from '../../hooks'
import type { OnFeaturesChange } from '../../types'
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'
import { fetchCodeBasedExtensionList } from '@/service/common'
import I18n from '@/context/i18n'
type ModerationProps = {
onChange?: OnFeaturesChange
disabled?: boolean
}
const Moderation = ({
onChange,
disabled,
}: ModerationProps) => {
const { t } = useTranslation()
const { setShowModerationSettingModal } = useModalContext()
const { locale } = useContext(I18n)
const featuresStore = useFeaturesStore()
const moderation = useFeatures(s => s.features.moderation)
const { data: codeBasedExtensionList } = useSWR(
'/code-based-extension?module=moderation',
fetchCodeBasedExtensionList,
)
const handleOpenModerationSettingModal = () => {
if (disabled)
return
const {
features,
setFeatures,
} = featuresStore!.getState()
setShowModerationSettingModal({
payload: moderation as any,
onSaveCallback: (newModeration) => {
const newFeatures = produce(features, (draft) => {
draft.moderation = newModeration
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
},
})
}
const renderInfo = () => {
let prefix = ''
let suffix = ''
if (moderation?.type === 'openai_moderation')
prefix = t('appDebug.feature.moderation.modal.provider.openai')
else if (moderation?.type === 'keywords')
prefix = t('appDebug.feature.moderation.modal.provider.keywords')
else if (moderation?.type === 'api')
prefix = t('common.apiBasedExtension.selector.title')
else
prefix = codeBasedExtensionList?.data.find(item => item.name === moderation?.type)?.label[locale] || ''
if (moderation?.config?.inputs_config?.enabled && moderation.config?.outputs_config?.enabled)
suffix = t('appDebug.feature.moderation.allEnabled')
else if (moderation?.config?.inputs_config?.enabled)
suffix = t('appDebug.feature.moderation.inputEnabled')
else if (moderation?.config?.outputs_config?.enabled)
suffix = t('appDebug.feature.moderation.outputEnabled')
return `${prefix} · ${suffix}`
}
return (
<div className='flex items-center px-3 h-12 bg-gray-50 rounded-xl overflow-hidden'>
<div className='shrink-0 flex items-center justify-center mr-1 w-6 h-6'>
<FileSearch02 className='shrink-0 w-4 h-4 text-[#039855]' />
</div>
<div className='shrink-0 mr-2 whitespace-nowrap text-sm text-gray-800 font-semibold'>
{t('appDebug.feature.moderation.title')}
</div>
<div
className='grow block w-0 text-right text-xs text-gray-500 truncate'
title={renderInfo()}>
{renderInfo()}
</div>
<div className='shrink-0 ml-4 mr-1 w-[1px] h-3.5 bg-gray-200'></div>
<div
className={`
shrink-0 flex items-center px-3 h-7 cursor-pointer rounded-md
text-xs text-gray-700 font-medium hover:bg-gray-200
${disabled && '!cursor-not-allowed'}
`}
onClick={handleOpenModerationSettingModal}
>
<Settings01 className='mr-[5px] w-3.5 h-3.5' />
{t('common.operation.settings')}
</div>
</div>
)
}
export default memo(Moderation)

View File

@@ -0,0 +1,73 @@
import type { FC } from 'react'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import Switch from '@/app/components/base/switch'
import type { ModerationContentConfig } from '@/models/debug'
type ModerationContentProps = {
title: string
info?: string
showPreset?: boolean
config: ModerationContentConfig
onConfigChange: (config: ModerationContentConfig) => void
}
const ModerationContent: FC<ModerationContentProps> = ({
title,
info,
showPreset = true,
config,
onConfigChange,
}) => {
const { t } = useTranslation()
const handleConfigChange = (field: string, value: boolean | string) => {
if (field === 'preset_response' && typeof value === 'string')
value = value.slice(0, 100)
onConfigChange({ ...config, [field]: value })
}
return (
<div className='py-2'>
<div className='rounded-lg bg-gray-50 border border-gray-200'>
<div className='flex items-center justify-between px-3 h-10 rounded-lg'>
<div className='shrink-0 text-sm font-medium text-gray-900'>{title}</div>
<div className='grow flex items-center justify-end'>
{
info && (
<div className='mr-2 text-xs text-gray-500 truncate' title={info}>{info}</div>
)
}
<Switch
size='l'
defaultValue={config.enabled}
onChange={v => handleConfigChange('enabled', v)}
/>
</div>
</div>
{
config.enabled && showPreset && (
<div className='px-3 pt-1 pb-3 bg-white rounded-lg'>
<div className='flex items-center justify-between h-8 text-[13px] font-medium text-gray-700'>
{t('appDebug.feature.moderation.modal.content.preset')}
<span className='text-xs font-normal text-gray-500'>{t('appDebug.feature.moderation.modal.content.supportMarkdown')}</span>
</div>
<div className='relative px-3 py-2 h-20 rounded-lg bg-gray-100'>
<textarea
value={config.preset_response || ''}
className='block w-full h-full bg-transparent text-sm outline-none appearance-none resize-none'
placeholder={t('appDebug.feature.moderation.modal.content.placeholder') || ''}
onChange={e => handleConfigChange('preset_response', e.target.value)}
/>
<div className='absolute bottom-2 right-2 flex items-center px-1 h-5 rounded-md bg-gray-50 text-xs font-medium text-gray-300'>
<span>{(config.preset_response || '').length}</span>/<span className='text-gray-500'>100</span>
</div>
</div>
</div>
)
}
</div>
</div>
)
}
export default memo(ModerationContent)

View File

@@ -0,0 +1,377 @@
import type { ChangeEvent, FC } from 'react'
import {
memo,
useState,
} from 'react'
import useSWR from 'swr'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import ModerationContent from './moderation-content'
import FormGeneration from './form-generation'
import ApiBasedExtensionSelector from '@/app/components/header/account-setting/api-based-extension-page/selector'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
import type { ModerationConfig, ModerationContentConfig } from '@/models/debug'
import { useToastContext } from '@/app/components/base/toast'
import {
fetchCodeBasedExtensionList,
fetchModelProviders,
} from '@/service/common'
import type { CodeBasedExtensionItem } from '@/models/common'
import I18n from '@/context/i18n'
import { LanguagesSupported } from '@/i18n/language'
import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general'
import { useModalContext } from '@/context/modal-context'
import { CustomConfigurationStatusEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
const systemTypes = ['openai_moderation', 'keywords', 'api']
type Provider = {
key: string
name: string
form_schema?: CodeBasedExtensionItem['form_schema']
}
type ModerationSettingModalProps = {
data: ModerationConfig
onCancel: () => void
onSave: (moderationConfig: ModerationConfig) => void
}
const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
data,
onCancel,
onSave,
}) => {
const { t } = useTranslation()
const { notify } = useToastContext()
const { locale } = useContext(I18n)
const { data: modelProviders, isLoading, mutate } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
const [localeData, setLocaleData] = useState<ModerationConfig>(data)
const { setShowAccountSettingModal } = useModalContext()
const handleOpenSettingsModal = () => {
setShowAccountSettingModal({
payload: 'provider',
onCancelCallback: () => {
mutate()
},
})
}
const { data: codeBasedExtensionList } = useSWR(
'/code-based-extension?module=moderation',
fetchCodeBasedExtensionList,
)
const openaiProvider = modelProviders?.data.find(item => item.provider === 'openai')
const systemOpenaiProviderEnabled = openaiProvider?.system_configuration.enabled
const systemOpenaiProviderQuota = systemOpenaiProviderEnabled ? openaiProvider?.system_configuration.quota_configurations.find(item => item.quota_type === openaiProvider.system_configuration.current_quota_type) : undefined
const systemOpenaiProviderCanUse = systemOpenaiProviderQuota?.is_valid
const customOpenaiProvidersCanUse = openaiProvider?.custom_configuration.status === CustomConfigurationStatusEnum.active
const openaiProviderConfiged = customOpenaiProvidersCanUse || systemOpenaiProviderCanUse
const providers: Provider[] = [
{
key: 'openai_moderation',
name: t('appDebug.feature.moderation.modal.provider.openai'),
},
{
key: 'keywords',
name: t('appDebug.feature.moderation.modal.provider.keywords'),
},
{
key: 'api',
name: t('common.apiBasedExtension.selector.title'),
},
...(
codeBasedExtensionList
? codeBasedExtensionList.data.map((item) => {
return {
key: item.name,
name: locale === 'zh-Hans' ? item.label['zh-Hans'] : item.label['en-US'],
form_schema: item.form_schema,
}
})
: []
),
]
const currentProvider = providers.find(provider => provider.key === localeData.type)
const handleDataTypeChange = (type: string) => {
let config: undefined | Record<string, any>
const currProvider = providers.find(provider => provider.key === type)
if (systemTypes.findIndex(t => t === type) < 0 && currProvider?.form_schema) {
config = currProvider?.form_schema.reduce((prev, next) => {
prev[next.variable] = next.default
return prev
}, {} as Record<string, any>)
}
setLocaleData({
...localeData,
type,
config,
})
}
const handleDataKeywordsChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
const value = e.target.value
const arr = value.split('\n').reduce((prev: string[], next: string) => {
if (next !== '')
prev.push(next.slice(0, 100))
if (next === '' && prev[prev.length - 1] !== '')
prev.push(next)
return prev
}, [])
setLocaleData({
...localeData,
config: {
...localeData.config,
keywords: arr.slice(0, 100).join('\n'),
},
})
}
const handleDataContentChange = (contentType: string, contentConfig: ModerationContentConfig) => {
setLocaleData({
...localeData,
config: {
...localeData.config,
[contentType]: contentConfig,
},
})
}
const handleDataApiBasedChange = (apiBasedExtensionId: string) => {
setLocaleData({
...localeData,
config: {
...localeData.config,
api_based_extension_id: apiBasedExtensionId,
},
})
}
const handleDataExtraChange = (extraValue: Record<string, string>) => {
setLocaleData({
...localeData,
config: {
...localeData.config,
...extraValue,
},
})
}
const formatData = (originData: ModerationConfig) => {
const { enabled, type, config } = originData
const { inputs_config, outputs_config } = config!
const params: Record<string, string | undefined> = {}
if (type === 'keywords')
params.keywords = config?.keywords
if (type === 'api')
params.api_based_extension_id = config?.api_based_extension_id
if (systemTypes.findIndex(t => t === type) < 0 && currentProvider?.form_schema) {
currentProvider.form_schema.forEach((form) => {
params[form.variable] = config?.[form.variable]
})
}
return {
type,
enabled,
config: {
inputs_config: inputs_config || { enabled: false },
outputs_config: outputs_config || { enabled: false },
...params,
},
}
}
const handleSave = () => {
if (localeData.type === 'openai_moderation' && !openaiProviderConfiged)
return
if (!localeData.config?.inputs_config?.enabled && !localeData.config?.outputs_config?.enabled) {
notify({ type: 'error', message: t('appDebug.feature.moderation.modal.content.condition') })
return
}
if (localeData.type === 'keywords' && !localeData.config.keywords) {
notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: locale !== LanguagesSupported[1] ? 'keywords' : '关键词' }) })
return
}
if (localeData.type === 'api' && !localeData.config.api_based_extension_id) {
notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: locale !== LanguagesSupported[1] ? 'API Extension' : 'API 扩展' }) })
return
}
if (systemTypes.findIndex(t => t === localeData.type) < 0 && currentProvider?.form_schema) {
for (let i = 0; i < currentProvider.form_schema.length; i++) {
if (!localeData.config?.[currentProvider.form_schema[i].variable] && currentProvider.form_schema[i].required) {
notify({
type: 'error',
message: t('appDebug.errorMessage.valueOfVarRequired', { key: locale !== LanguagesSupported[1] ? currentProvider.form_schema[i].label['en-US'] : currentProvider.form_schema[i].label['zh-Hans'] }),
})
return
}
}
}
if (localeData.config.inputs_config?.enabled && !localeData.config.inputs_config.preset_response && localeData.type !== 'api') {
notify({ type: 'error', message: t('appDebug.feature.moderation.modal.content.errorMessage') })
return
}
if (localeData.config.outputs_config?.enabled && !localeData.config.outputs_config.preset_response && localeData.type !== 'api') {
notify({ type: 'error', message: t('appDebug.feature.moderation.modal.content.errorMessage') })
return
}
onSave(formatData(localeData))
}
return (
<Modal
isShow
onClose={() => { }}
className='!p-8 !pb-6 !mt-14 !max-w-none !w-[640px]'
>
<div className='mb-2 text-xl font-semibold text-[#1D2939]'>
{t('appDebug.feature.moderation.modal.title')}
</div>
<div className='py-2'>
<div className='leading-9 text-sm font-medium text-gray-900'>
{t('appDebug.feature.moderation.modal.provider.title')}
</div>
<div className='grid gap-2.5 grid-cols-3'>
{
providers.map(provider => (
<div
key={provider.key}
className={`
flex items-center px-3 py-2 rounded-lg text-sm text-gray-900 cursor-pointer
${localeData.type === provider.key ? 'bg-white border-[1.5px] border-primary-400 shadow-sm' : 'border border-gray-100 bg-gray-25'}
${localeData.type === 'openai_moderation' && provider.key === 'openai_moderation' && !openaiProviderConfiged && 'opacity-50'}
`}
onClick={() => handleDataTypeChange(provider.key)}
>
<div className={`
mr-2 w-4 h-4 rounded-full border
${localeData.type === provider.key ? 'border-[5px] border-primary-600' : 'border border-gray-300'}`} />
{provider.name}
</div>
))
}
</div>
{
!isLoading && !openaiProviderConfiged && localeData.type === 'openai_moderation' && (
<div className='flex items-center mt-2 px-3 py-2 bg-[#FFFAEB] rounded-lg border border-[#FEF0C7]'>
<InfoCircle className='mr-1 w-4 h-4 text-[#F79009]' />
<div className='flex items-center text-xs font-medium text-gray-700'>
{t('appDebug.feature.moderation.modal.openaiNotConfig.before')}
<span
className='text-primary-600 cursor-pointer'
onClick={handleOpenSettingsModal}
>
&nbsp;{t('common.settings.provider')}&nbsp;
</span>
{t('appDebug.feature.moderation.modal.openaiNotConfig.after')}
</div>
</div>
)
}
</div>
{
localeData.type === 'keywords' && (
<div className='py-2'>
<div className='mb-1 text-sm font-medium text-gray-900'>{t('appDebug.feature.moderation.modal.provider.keywords')}</div>
<div className='mb-2 text-xs text-gray-500'>{t('appDebug.feature.moderation.modal.keywords.tip')}</div>
<div className='relative px-3 py-2 h-[88px] bg-gray-100 rounded-lg'>
<textarea
value={localeData.config?.keywords || ''}
onChange={handleDataKeywordsChange}
className='block w-full h-full bg-transparent text-sm outline-none appearance-none resize-none'
placeholder={t('appDebug.feature.moderation.modal.keywords.placeholder') || ''}
/>
<div className='absolute bottom-2 right-2 flex items-center px-1 h-5 rounded-md bg-gray-50 text-xs font-medium text-gray-300'>
<span>{(localeData.config?.keywords || '').split('\n').filter(Boolean).length}</span>/<span className='text-gray-500'>100 {t('appDebug.feature.moderation.modal.keywords.line')}</span>
</div>
</div>
</div>
)
}
{
localeData.type === 'api' && (
<div className='py-2'>
<div className='flex items-center justify-between h-9'>
<div className='text-sm font-medium text-gray-900'>{t('common.apiBasedExtension.selector.title')}</div>
<a
href={t('common.apiBasedExtension.linkUrl') || '/'}
target='_blank' rel='noopener noreferrer'
className='group flex items-center text-xs text-gray-500 hover:text-primary-600'
>
<BookOpen01 className='mr-1 w-3 h-3 text-gray-500 group-hover:text-primary-600' />
{t('common.apiBasedExtension.link')}
</a>
</div>
<ApiBasedExtensionSelector
value={localeData.config?.api_based_extension_id || ''}
onChange={handleDataApiBasedChange}
/>
</div>
)
}
{
systemTypes.findIndex(t => t === localeData.type) < 0
&& currentProvider?.form_schema
&& (
<FormGeneration
forms={currentProvider?.form_schema}
value={localeData.config}
onChange={handleDataExtraChange}
/>
)
}
<div className='my-3 h-[1px] bg-gradient-to-r from-[#F3F4F6]'></div>
<ModerationContent
title={t('appDebug.feature.moderation.modal.content.input') || ''}
config={localeData.config?.inputs_config || { enabled: false, preset_response: '' }}
onConfigChange={config => handleDataContentChange('inputs_config', config)}
info={(localeData.type === 'api' && t('appDebug.feature.moderation.modal.content.fromApi')) || ''}
showPreset={!(localeData.type === 'api')}
/>
<ModerationContent
title={t('appDebug.feature.moderation.modal.content.output') || ''}
config={localeData.config?.outputs_config || { enabled: false, preset_response: '' }}
onConfigChange={config => handleDataContentChange('outputs_config', config)}
info={(localeData.type === 'api' && t('appDebug.feature.moderation.modal.content.fromApi')) || ''}
showPreset={!(localeData.type === 'api')}
/>
<div className='mt-1 mb-8 text-xs font-medium text-gray-500'>{t('appDebug.feature.moderation.modal.content.condition')}</div>
<div className='flex items-center justify-end'>
<Button
onClick={onCancel}
className='mr-2 text-sm font-medium'
>
{t('common.operation.cancel')}
</Button>
<Button
type='primary'
className='text-sm font-medium'
onClick={handleSave}
disabled={localeData.type === 'openai_moderation' && !openaiProviderConfiged}
>
{t('common.operation.save')}
</Button>
</div>
</Modal>
)
}
export default memo(ModerationSettingModal)

View File

@@ -0,0 +1,312 @@
/* eslint-disable multiline-ternary */
'use client'
import type { FC } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import produce from 'immer'
import cn from 'classnames'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import { ReactSortable } from 'react-sortablejs'
import {
useFeatures,
useFeaturesStore,
} from '../../hooks'
import type { OnFeaturesChange } from '../../types'
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'
import { getInputKeys } from '@/app/components/base/block-input'
import ConfirmAddVar from '@/app/components/app/configuration/config-prompt/confirm-add-var'
import { getNewVar } from '@/utils/var'
import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight'
import { Plus, Trash03 } from '@/app/components/base/icons/src/vender/line/general'
import type { PromptVariable } from '@/models/debug'
const MAX_QUESTION_NUM = 5
export type OpeningStatementProps = {
onChange?: OnFeaturesChange
readonly?: boolean
promptVariables?: PromptVariable[]
onAutoAddPromptVariable: (variable: PromptVariable[]) => void
}
// regex to match the {{}} and replace it with a span
const regex = /\{\{([^}]+)\}\}/g
const OpeningStatement: FC<OpeningStatementProps> = ({
onChange,
readonly,
promptVariables = [],
onAutoAddPromptVariable,
}) => {
const { t } = useTranslation()
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[]>([])
const hasValue = !!(value || '').trim()
const inputRef = useRef<HTMLTextAreaElement>(null)
const [isFocus, { setTrue: didSetFocus, setFalse: setBlur }] = useBoolean(false)
const setFocus = () => {
didSetFocus()
setTimeout(() => {
const input = inputRef.current
if (input) {
input.focus()
input.setSelectionRange(input.value.length, input.value.length)
}
}, 0)
}
const [tempValue, setTempValue] = useState(value)
useEffect(() => {
setTempValue(value || '')
}, [value])
const [tempSuggestedQuestions, setTempSuggestedQuestions] = useState(suggestedQuestions || [])
const notEmptyQuestions = tempSuggestedQuestions.filter(question => !!question && question.trim())
const coloredContent = (tempValue || '')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(regex, varHighlightHTML({ name: '$1' })) // `<span class="${highLightClassName}">{{$1}}</span>`
.replace(/\n/g, '<br />')
const handleEdit = () => {
if (readonly)
return
setFocus()
}
const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false)
const handleCancel = () => {
setBlur()
setTempValue(value)
setTempSuggestedQuestions(suggestedQuestions)
}
const handleConfirm = () => {
const keys = getInputKeys(tempValue)
const promptKeys = promptVariables.map(item => item.key)
let notIncludeKeys: string[] = []
if (promptKeys.length === 0) {
if (keys.length > 0)
notIncludeKeys = keys
}
else {
notIncludeKeys = keys.filter(key => !promptKeys.includes(key))
}
if (notIncludeKeys.length > 0) {
setNotIncludeKeys(notIncludeKeys)
showConfirmAddVar()
return
}
setBlur()
const { getState } = featureStore!
const {
features,
setFeatures,
} = getState()
const newFeatures = produce(features, (draft) => {
if (draft.opening) {
draft.opening.opening_statement = tempValue
draft.opening.suggested_questions = tempSuggestedQuestions
}
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
}
const cancelAutoAddVar = () => {
const { getState } = featureStore!
const {
features,
setFeatures,
} = getState()
const newFeatures = produce(features, (draft) => {
if (draft.opening)
draft.opening.opening_statement = tempValue
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
hideConfirmAddVar()
setBlur()
}
const autoAddVar = () => {
const { getState } = featureStore!
const {
features,
setFeatures,
} = getState()
const newFeatures = produce(features, (draft) => {
if (draft.opening)
draft.opening.opening_statement = tempValue
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
onAutoAddPromptVariable([...notIncludeKeys.map(key => getNewVar(key, 'string'))])
hideConfirmAddVar()
setBlur()
}
const headerRight = !readonly ? (
isFocus ? (
<div className='flex items-center space-x-1'>
<div className='px-3 leading-[18px] text-xs font-medium text-gray-700 cursor-pointer' onClick={handleCancel}>{t('common.operation.cancel')}</div>
<Button className='!h-8 !px-3 text-xs' onClick={handleConfirm} type="primary">{t('common.operation.save')}</Button>
</div>
) : (
<OperationBtn type='edit' actionName={hasValue ? '' : t('appDebug.openingStatement.writeOpner') as string} onClick={handleEdit} />
)
) : null
const renderQuestions = () => {
return isFocus ? (
<div>
<div className='flex items-center py-2'>
<div className='shrink-0 flex space-x-0.5 leading-[18px] text-xs font-medium text-gray-500'>
<div className='uppercase'>{t('appDebug.openingStatement.openingQuestion')}</div>
<div>·</div>
<div>{tempSuggestedQuestions.length}/{MAX_QUESTION_NUM}</div>
</div>
<div className='ml-3 grow w-0 h-px bg-[#243, 244, 246]'></div>
</div>
<ReactSortable
className="space-y-1"
list={tempSuggestedQuestions.map((name, index) => {
return {
id: index,
name,
}
})}
setList={list => setTempSuggestedQuestions(list.map(item => item.name))}
handle='.handle'
ghostClass="opacity-50"
animation={150}
>
{tempSuggestedQuestions.map((question, index) => {
return (
<div className='group relative rounded-lg border border-gray-200 flex items-center pl-2.5 hover:border-gray-300 hover:bg-white' key={index}>
<div className='handle flex items-center justify-center w-4 h-4 cursor-grab'>
<svg width="6" height="10" viewBox="0 0 6 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M1 2C1.55228 2 2 1.55228 2 1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1C0 1.55228 0.447715 2 1 2ZM1 6C1.55228 6 2 5.55228 2 5C2 4.44772 1.55228 4 1 4C0.447715 4 0 4.44772 0 5C0 5.55228 0.447715 6 1 6ZM6 1C6 1.55228 5.55228 2 5 2C4.44772 2 4 1.55228 4 1C4 0.447715 4.44772 0 5 0C5.55228 0 6 0.447715 6 1ZM5 6C5.55228 6 6 5.55228 6 5C6 4.44772 5.55228 4 5 4C4.44772 4 4 4.44772 4 5C4 5.55228 4.44772 6 5 6ZM2 9C2 9.55229 1.55228 10 1 10C0.447715 10 0 9.55229 0 9C0 8.44771 0.447715 8 1 8C1.55228 8 2 8.44771 2 9ZM5 10C5.55228 10 6 9.55229 6 9C6 8.44771 5.55228 8 5 8C4.44772 8 4 8.44771 4 9C4 9.55229 4.44772 10 5 10Z" fill="#98A2B3" />
</svg>
</div>
<input
type="input"
value={question || ''}
onChange={(e) => {
const value = e.target.value
setTempSuggestedQuestions(tempSuggestedQuestions.map((item, i) => {
if (index === i)
return value
return item
}))
}}
className={'w-full overflow-x-auto pl-1.5 pr-8 text-sm leading-9 text-gray-900 border-0 grow h-9 bg-transparent focus:outline-none cursor-pointer rounded-lg'}
/>
<div
className='block absolute top-1/2 translate-y-[-50%] right-1.5 p-1 rounded-md cursor-pointer hover:bg-[#FEE4E2] hover:text-[#D92D20]'
onClick={() => {
setTempSuggestedQuestions(tempSuggestedQuestions.filter((_, i) => index !== i))
}}
>
<Trash03 className='w-3.5 h-3.5' />
</div>
</div>
)
})}</ReactSortable>
{tempSuggestedQuestions.length < MAX_QUESTION_NUM && (
<div
onClick={() => { setTempSuggestedQuestions([...tempSuggestedQuestions, '']) }}
className='mt-1 flex items-center h-9 px-3 gap-2 rounded-lg cursor-pointer text-gray-400 bg-gray-100 hover:bg-gray-200'>
<Plus className='w-4 h-4'></Plus>
<div className='text-gray-500 text-[13px]'>{t('appDebug.variableConig.addOption')}</div>
</div>
)}
</div>
) : (
<div className='mt-1.5 flex flex-wrap'>
{notEmptyQuestions.map((question, index) => {
return (
<div key={index} className='mt-1 mr-1 max-w-full truncate last:mr-0 shrink-0 leading-8 items-center px-2.5 rounded-lg border border-gray-200 shadow-xs bg-white text-[13px] font-normal text-gray-900 cursor-pointer'>
{question}
</div>
)
})}
</div>
)
}
return (
<Panel
className={cn(isShowConfirmAddVar && 'h-[220px]', 'relative !bg-gray-25')}
title={t('appDebug.openingStatement.title')}
headerIcon={
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fillRule="evenodd" clipRule="evenodd" d="M8.33353 1.33301C4.83572 1.33301 2.00019 4.16854 2.00019 7.66634C2.00019 8.37301 2.11619 9.05395 2.3307 9.69036C2.36843 9.80229 2.39063 9.86853 2.40507 9.91738L2.40979 9.93383L2.40729 9.93903C2.39015 9.97437 2.36469 10.0218 2.31705 10.11L1.2158 12.1484C1.14755 12.2746 1.07633 12.4064 1.02735 12.5209C0.978668 12.6348 0.899813 12.8437 0.938613 13.0914C0.984094 13.3817 1.15495 13.6373 1.40581 13.7903C1.61981 13.9208 1.843 13.9279 1.96683 13.9264C2.09141 13.925 2.24036 13.9095 2.38314 13.8947L5.81978 13.5395C5.87482 13.5338 5.9036 13.5309 5.92468 13.5292L5.92739 13.529L5.93564 13.532C5.96154 13.5413 5.99666 13.5548 6.0573 13.5781C6.76459 13.8506 7.53244 13.9997 8.33353 13.9997C11.8313 13.9997 14.6669 11.1641 14.6669 7.66634C14.6669 4.16854 11.8313 1.33301 8.33353 1.33301ZM5.9799 5.72116C6.73142 5.08698 7.73164 5.27327 8.33144 5.96584C8.93125 5.27327 9.91854 5.09365 10.683 5.72116C11.4474 6.34867 11.5403 7.41567 10.9501 8.16572C10.5845 8.6304 9.6668 9.47911 9.02142 10.0576C8.78435 10.2702 8.66582 10.3764 8.52357 10.4192C8.40154 10.456 8.26134 10.456 8.13931 10.4192C7.99706 10.3764 7.87853 10.2702 7.64147 10.0576C6.99609 9.47911 6.07839 8.6304 5.71276 8.16572C5.12259 7.41567 5.22839 6.35534 5.9799 5.72116Z" fill="#E74694" />
</svg>
}
headerRight={headerRight}
hasHeaderBottomBorder={!hasValue}
isFocus={isFocus}
>
<div className='text-gray-700 text-sm'>
{(hasValue || (!hasValue && isFocus)) ? (
<>
{isFocus
? (
<div>
<textarea
ref={inputRef}
value={tempValue}
rows={3}
onChange={e => setTempValue(e.target.value)}
className="w-full px-0 text-sm border-0 bg-transparent focus:outline-none "
placeholder={t('appDebug.openingStatement.placeholder') as string}
>
</textarea>
</div>
)
: (
<div dangerouslySetInnerHTML={{
__html: coloredContent,
}}></div>
)}
{renderQuestions()}
</>) : (
<div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.openingStatement.noDataPlaceHolder')}</div>
)}
{isShowConfirmAddVar && (
<ConfirmAddVar
varNameArr={notIncludeKeys}
onConfrim={autoAddVar}
onCancel={cancelAutoAddVar}
onHide={hideConfirmAddVar}
/>
)}
</div>
</Panel>
)
}
export default React.memo(OpeningStatement)

View File

@@ -0,0 +1,38 @@
import ReactSlider from 'react-slider'
import cn from 'classnames'
import s from './style.module.css'
type ISliderProps = {
className?: string
value: number
max?: number
min?: number
step?: number
disabled?: boolean
onChange: (value: number) => void
}
const Slider: React.FC<ISliderProps> = ({ className, max, min, step, value, disabled, onChange }) => {
return <ReactSlider
disabled={disabled}
value={isNaN(value) ? 0 : value}
min={min || 0}
max={max || 100}
step={step || 1}
className={cn(className, s.slider)}
thumbClassName={cn(s['slider-thumb'], 'top-[-7px] w-2 h-[18px] bg-white border !border-black/8 rounded-[36px] shadow-md cursor-pointer')}
trackClassName={s['slider-track']}
onChange={onChange}
renderThumb={(props, state) => (
<div {...props}>
<div className='relative w-full h-full'>
<div className='absolute top-[-16px] left-[50%] translate-x-[-50%] leading-[18px] text-xs font-medium text-gray-900'>
{(state.valueNow / 100).toFixed(2)}
</div>
</div>
</div>
)}
/>
}
export default Slider

View File

@@ -0,0 +1,20 @@
.slider {
position: relative;
}
.slider.disabled {
opacity: 0.6;
}
.slider-thumb:focus {
outline: none;
}
.slider-track {
background-color: #528BFF;
height: 2px;
}
.slider-track-1 {
background-color: #E5E7EB;
}

View File

@@ -0,0 +1,46 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Slider from '@/app/components/app/configuration/toolbox/score-slider/base-slider'
type Props = {
className?: string
value: number
onChange: (value: number) => void
}
const ScoreSlider: FC<Props> = ({
className,
value,
onChange,
}) => {
const { t } = useTranslation()
return (
<div className={className}>
<div className='h-[1px] mt-[14px]'>
<Slider
max={100}
min={80}
step={1}
value={value}
onChange={onChange}
/>
</div>
<div className='mt-[10px] flex justify-between items-center leading-4 text-xs font-normal '>
<div className='flex space-x-1 text-[#00A286]'>
<div>0.8</div>
<div>·</div>
<div>{t('appDebug.feature.annotation.scoreThreshold.easyMatch')}</div>
</div>
<div className='flex space-x-1 text-[#0057D8]'>
<div>1.0</div>
<div>·</div>
<div>{t('appDebug.feature.annotation.scoreThreshold.accurateMatch')}</div>
</div>
</div>
</div>
)
}
export default React.memo(ScoreSlider)

View File

@@ -0,0 +1,22 @@
'use client'
import React, { type FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Microphone01 } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
const SpeechToTextConfig: FC = () => {
const { t } = useTranslation()
return (
<div className='flex items-center px-3 h-12 bg-gray-50 rounded-xl overflow-hidden'>
<div className='shrink-0 flex items-center justify-center mr-1 w-6 h-6'>
<Microphone01 className='w-4 h-4 text-[#7839EE]' />
</div>
<div className='shrink-0 mr-2 flex items-center whitespace-nowrap text-sm text-gray-800 font-semibold'>
<div>{t('appDebug.feature.speechToText.title')}</div>
</div>
<div className='grow'></div>
<div className='text-xs text-gray-500'>{t('appDebug.feature.speechToText.resDes')}</div>
</div>
)
}
export default React.memo(SpeechToTextConfig)

View File

@@ -0,0 +1,28 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general'
import { MessageSmileSquare } from '@/app/components/base/icons/src/vender/solid/communication'
import TooltipPlus from '@/app/components/base/tooltip-plus'
const SuggestedQuestionsAfterAnswer: FC = () => {
const { t } = useTranslation()
return (
<div className='flex items-center px-3 h-12 bg-gray-50 rounded-xl overflow-hidden'>
<div className='shrink-0 flex items-center justify-center mr-1 w-6 h-6'>
<MessageSmileSquare className='w-4 h-4 text-[#06AED4]' />
</div>
<div className='shrink-0 mr-2 flex items-center whitespace-nowrap text-sm text-gray-800 font-semibold'>
<div className='mr-2'>{t('appDebug.feature.suggestedQuestionsAfterAnswer.title')}</div>
<TooltipPlus popupContent={t('appDebug.feature.suggestedQuestionsAfterAnswer.description')}>
<HelpCircle className='w-[14px] h-[14px] text-gray-400' />
</TooltipPlus>
</div>
<div className='grow'></div>
<div className='text-xs text-gray-500'>{t('appDebug.feature.suggestedQuestionsAfterAnswer.resDes')}</div>
</div>
)
}
export default React.memo(SuggestedQuestionsAfterAnswer)

View File

@@ -0,0 +1,60 @@
'use client'
import useSWR from 'swr'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { usePathname } from 'next/navigation'
import { useFeatures } from '../../hooks'
import type { OnFeaturesChange } from '../../types'
import ParamsConfig from './params-config'
import { Speaker } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
import { languages } from '@/i18n/language'
import { fetchAppVoices } from '@/service/apps'
import AudioBtn from '@/app/components/base/audio-btn'
type TextToSpeechProps = {
onChange?: OnFeaturesChange
disabled?: boolean
}
const TextToSpeech = ({
onChange,
disabled,
}: TextToSpeechProps) => {
const { t } = useTranslation()
const textToSpeech = useFeatures(s => s.features.text2speech)
const pathname = usePathname()
const matched = pathname.match(/\/app\/([^/]+)/)
const appId = (matched?.length && matched[1]) ? matched[1] : ''
const language = textToSpeech?.language
const languageInfo = languages.find(i => i.value === textToSpeech?.language)
const voiceItems = useSWR({ appId, language }, fetchAppVoices).data
const voiceItem = voiceItems?.find(item => item.value === textToSpeech?.voice)
return (
<div className='flex items-center px-3 h-12 bg-gray-50 rounded-xl overflow-hidden'>
<div className='shrink-0 flex items-center justify-center mr-1 w-6 h-6'>
<Speaker className='w-4 h-4 text-[#7839EE]' />
</div>
<div className='shrink-0 mr-2 whitespace-nowrap text-sm text-gray-800 font-semibold'>
{t('appDebug.feature.textToSpeech.title')}
</div>
<div
className='grow '>
</div>
<div className='shrink-0 text-xs text-gray-500 inline-flex items-center gap-2'>
{languageInfo && (`${languageInfo?.name} - `)}{voiceItem?.name ?? t('appDebug.voice.defaultDisplay')}
{ languageInfo?.example && (
<AudioBtn
value={languageInfo?.example}
isAudition={true}
/>
)}
</div>
<div className='shrink-0 flex items-center'>
<ParamsConfig onChange={onChange} disabled={disabled} />
</div>
</div>
)
}
export default React.memo(TextToSpeech)

View File

@@ -0,0 +1,203 @@
'use client'
import useSWR from 'swr'
import produce from 'immer'
import React, { Fragment } from 'react'
import classNames from 'classnames'
import { usePathname } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import { Listbox, Transition } from '@headlessui/react'
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid'
import {
useFeatures,
useFeaturesStore,
} from '../../hooks'
import type { OnFeaturesChange } from '../../types'
import type { Item } from '@/app/components/base/select'
import { fetchAppVoices } from '@/service/apps'
import Tooltip from '@/app/components/base/tooltip'
import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general'
import { languages } from '@/i18n/language'
type VoiceParamConfigProps = {
onChange?: OnFeaturesChange
}
const VoiceParamConfig = ({
onChange,
}: VoiceParamConfigProps) => {
const { t } = useTranslation()
const pathname = usePathname()
const matched = pathname.match(/\/app\/([^/]+)/)
const appId = (matched?.length && matched[1]) ? matched[1] : ''
const text2speech = useFeatures(state => state.features.text2speech)
const featuresStore = useFeaturesStore()
const languageItem = languages.find(item => item.value === text2speech.language)
const localLanguagePlaceholder = languageItem?.name || t('common.placeholder.select')
const language = languageItem?.value
const voiceItems = useSWR({ appId, language }, fetchAppVoices).data
const voiceItem = voiceItems?.find(item => item.value === text2speech.voice)
const localVoicePlaceholder = voiceItem?.name || t('common.placeholder.select')
const handleChange = (value: Record<string, string>) => {
const {
features,
setFeatures,
} = featuresStore!.getState()
const newFeatures = produce(features, (draft) => {
draft.text2speech = {
...draft.text2speech,
...value,
}
})
setFeatures(newFeatures)
if (onChange)
onChange(newFeatures)
}
return (
<div>
<div>
<div className='leading-6 text-base font-semibold text-gray-800'>{t('appDebug.voice.voiceSettings.title')}</div>
<div className='pt-3 space-y-6'>
<div>
<div className='mb-2 flex items-center space-x-1'>
<div className='leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.voice.voiceSettings.language')}</div>
<Tooltip htmlContent={<div className='w-[180px]' >
{t('appDebug.voice.voiceSettings.resolutionTooltip').split('\n').map(item => (
<div key={item}>{item}</div>
))}
</div>} selector='config-resolution-tooltip'>
<HelpCircle className='w-[14px] h-[14px] text-gray-400' />
</Tooltip>
</div>
<Listbox
value={languageItem}
onChange={(value: Item) => {
handleChange({
language: String(value.value),
})
}}
>
<div className={'relative h-9'}>
<Listbox.Button className={'w-full h-full rounded-lg border-0 bg-gray-100 py-1.5 pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200 cursor-pointer'}>
<span className={classNames('block truncate text-left', !languageItem?.name && 'text-gray-400')}>
{languageItem?.name ? t(`common.voice.language.${languageItem?.value.replace('-', '')}`) : localLanguagePlaceholder}
</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronDownIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm">
{languages.map((item: Item) => (
<Listbox.Option
key={item.value}
className={({ active }) =>
`relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : ''
}`
}
value={item}
disabled={false}
>
{({ /* active, */ selected }) => (
<>
<span
className={classNames('block', selected && 'font-normal')}>{t(`common.voice.language.${(item.value).toString().replace('-', '')}`)}</span>
{(selected || item.value === text2speech.language) && (
<span
className={classNames(
'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700',
)}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
)}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
</div>
<div>
<div className='mb-2 leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.voice.voiceSettings.voice')}</div>
<Listbox
value={voiceItem}
disabled={!languageItem}
onChange={(value: Item) => {
handleChange({
voice: String(value.value),
})
}}
>
<div className={'relative h-9'}>
<Listbox.Button className={'w-full h-full rounded-lg border-0 bg-gray-100 py-1.5 pl-3 pr-10 sm:text-sm sm:leading-6 focus-visible:outline-none focus-visible:bg-gray-200 group-hover:bg-gray-200 cursor-pointer'}>
<span className={classNames('block truncate text-left', !voiceItem?.name && 'text-gray-400')}>{voiceItem?.name ?? localVoicePlaceholder}</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronDownIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute z-10 mt-1 px-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg border-gray-200 border-[0.5px] focus:outline-none sm:text-sm">
{voiceItems?.map((item: Item) => (
<Listbox.Option
key={item.value}
className={({ active }) =>
`relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : ''
}`
}
value={item}
disabled={false}
>
{({ /* active, */ selected }) => (
<>
<span className={classNames('block', selected && 'font-normal')}>{item.name}</span>
{(selected || item.value === text2speech.voice) && (
<span
className={classNames(
'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700',
)}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
)}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</Listbox>
</div>
</div>
</div>
</div>
)
}
export default React.memo(VoiceParamConfig)

View File

@@ -0,0 +1,48 @@
'use client'
import { memo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
import type { OnFeaturesChange } from '../../types'
import ParamConfigContent from './param-config-content'
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
type ParamsConfigProps = {
onChange?: OnFeaturesChange
disabled?: boolean
}
const ParamsConfig = ({
onChange,
disabled,
}: ParamsConfigProps) => {
const { t } = useTranslation()
const [open, setOpen] = useState(false)
return (
<PortalToFollowElem
open={open}
onOpenChange={setOpen}
placement='bottom-end'
offset={{
mainAxis: 4,
}}
>
<PortalToFollowElemTrigger onClick={() => !disabled && setOpen(v => !v)}>
<div className={cn('flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}>
<Settings01 className='w-3.5 h-3.5 ' />
<div className='ml-1 leading-[18px] text-xs font-medium '>{t('appDebug.voice.settings')}</div>
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent style={{ zIndex: 50 }}>
<div className='w-80 sm:w-[412px] p-4 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg space-y-3'>
<ParamConfigContent onChange={onChange} />
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default memo(ParamsConfig)

View File

@@ -0,0 +1,16 @@
import { useContext } from 'react'
import { useStore } from 'zustand'
import { FeaturesContext } from './context'
import type { FeatureStoreState } from './store'
export function useFeatures<T>(selector: (state: FeatureStoreState) => T): T {
const store = useContext(FeaturesContext)
if (!store)
throw new Error('Missing FeaturesContext.Provider in the tree')
return useStore(store, selector)
}
export function useFeaturesStore() {
return useContext(FeaturesContext)
}

View File

@@ -0,0 +1,3 @@
export { default as FeaturesPanel } from './feature-panel'
export { default as FeaturesChoose } from './feature-choose'
export { FeaturesProvider } from './context'

View File

@@ -0,0 +1,59 @@
import { createStore } from 'zustand'
import type { Features } from './types'
import { TransferMethod } from '@/types/app'
export type FeaturesModal = {
showFeaturesModal: boolean
setShowFeaturesModal: (showFeaturesModal: boolean) => void
}
export type FeaturesState = {
features: Features
}
export type FeaturesAction = {
setFeatures: (features: Features) => void
}
export type FeatureStoreState = FeaturesState & FeaturesAction & FeaturesModal
export type FeaturesStore = ReturnType<typeof createFeaturesStore>
export const createFeaturesStore = (initProps?: Partial<FeaturesState>) => {
const DEFAULT_PROPS: FeaturesState = {
features: {
opening: {
enabled: false,
},
suggested: {
enabled: false,
},
text2speech: {
enabled: false,
},
speech2text: {
enabled: false,
},
citation: {
enabled: false,
},
moderation: {
enabled: false,
},
file: {
image: {
enabled: false,
number_limits: 3,
transfer_methods: [TransferMethod.local_file, TransferMethod.remote_url],
},
},
},
}
return createStore<FeatureStoreState>()(set => ({
...DEFAULT_PROPS,
...initProps,
setFeatures: features => set(() => ({ features })),
showFeaturesModal: false,
setShowFeaturesModal: showFeaturesModal => set(() => ({ showFeaturesModal })),
}))
}

View File

@@ -0,0 +1,55 @@
import type { TransferMethod } from '@/types/app'
export type EnabledOrDisabled = {
enabled?: boolean
}
export type OpeningStatement = EnabledOrDisabled & {
opening_statement?: string
suggested_questions?: string[]
}
export type SuggestedQuestionsAfterAnswer = EnabledOrDisabled
export type TextToSpeech = EnabledOrDisabled & {
language?: string
voice?: string
}
export type SpeechToText = EnabledOrDisabled
export type RetrieverResource = EnabledOrDisabled
export type SensitiveWordAvoidance = EnabledOrDisabled & {
type?: string
config?: any
}
export type FileUpload = {
image?: EnabledOrDisabled & {
number_limits?: number
transfer_methods?: TransferMethod[]
}
}
export enum FeatureEnum {
opening = 'opening',
suggested = 'suggested',
text2speech = 'text2speech',
speech2text = 'speech2text',
citation = 'citation',
moderation = 'moderation',
file = 'file',
}
export type Features = {
[FeatureEnum.opening]?: OpeningStatement
[FeatureEnum.suggested]?: SuggestedQuestionsAfterAnswer
[FeatureEnum.text2speech]?: TextToSpeech
[FeatureEnum.speech2text]?: SpeechToText
[FeatureEnum.citation]?: RetrieverResource
[FeatureEnum.moderation]?: SensitiveWordAvoidance
[FeatureEnum.file]?: FileUpload
}
export type OnFeaturesChange = (features: Features) => void

View File

@@ -0,0 +1,3 @@
<svg width="5" height="12" viewBox="0 0 5 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Line 3" d="M1 11.3545L3.94174 0.645781" stroke="#D0D5DD" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 193 B

View File

@@ -0,0 +1 @@
<svg fill="none" height="26" viewBox="0 0 24 26" width="24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><filter id="a" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse" height="26" width="22" x="1" y="0"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="1"/><feGaussianBlur stdDeviation="1"/><feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.0941176 0 0 0 0 0.156863 0 0 0 0.05 0"/><feBlend in2="BackgroundImageFix" mode="normal" result="effect1_dropShadow_7605_8828"/><feBlend in="SourceGraphic" in2="effect1_dropShadow_7605_8828" mode="normal" result="shape"/></filter><g filter="url(#a)"><path d="m3 5.8c0-1.68016 0-2.52024.32698-3.16197.28762-.56449.74656-1.02343 1.31105-1.31105.64173-.32698 1.48181-.32698 3.16197-.32698h6.2l7 7v10.2c0 1.6802 0 2.5202-.327 3.162-.2876.5645-.7465 1.0234-1.311 1.311-.6418.327-1.4818.327-3.162.327h-8.4c-1.68016 0-2.52024 0-3.16197-.327-.56449-.2876-1.02343-.7465-1.31105-1.311-.32698-.6418-.32698-1.4818-.32698-3.162z" fill="#e8eaed"/><path d="m16.2 22.75h-8.4c-.8442 0-1.46232-.0002-1.95004-.04-.48479-.0397-.81868-.1172-1.09843-.2597-.51745-.2637-.93815-.6844-1.2018-1.2018-.14254-.2798-.22008-.6137-.25969-1.0985-.03985-.4877-.04004-1.1058-.04004-1.95v-12.4c0-.8442.00019-1.46232.04004-1.95004.03961-.48479.11715-.81868.25969-1.09843.26365-.51745.68435-.93815 1.2018-1.2018.27975-.14254.61364-.22008 1.09843-.25969.48772-.03985 1.10584-.04004 1.95004-.04004h6.0964l6.8536 6.85355v10.09645c0 .8442-.0002 1.4623-.04 1.95-.0397.4848-.1172.8187-.2597 1.0985-.2637.5174-.6844.9381-1.2018 1.2018-.2798.1425-.6137.22-1.0985.2597-.4877.0398-1.1058.04-1.95.04z" stroke="#000" stroke-opacity=".03" stroke-width=".5"/></g><path d="m14 1 7 7h-5c-1.1046 0-2-.89543-2-2z" fill="#fff" opacity=".5"/><path d="m11.5264 9-2.15191 3.2267v2.0455h-1.31897v-2.0455l-2.05552-3.2267h1.48242l1.30707 2.0776 1.31781-2.0776z" fill="#000"/><path d="m13.7426 13.1121h-2.3874l-.4855 1.1724h-1.0572l2.2355-5.27223h1.0813l2.1448 5.27223h-1.1297zm-.3966-1.0526-.7318-1.9348-.8165 1.9348z" fill="#cb171e"/><g fill="#000"><path d="m8.05469 14.8635v5.1673h1.10866v-3.5643l1.16025 2.3957h.8727l1.1999-2.4799v3.6474h1.0636v-5.1662h-1.4522l-1.2885 2.3369-1.22722-2.3369z"/><path d="m17.9994 18.9079h-2.7272v-4.0456h-1.1296v5.1451h3.8568z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 14H10M10 14V20M10 14L3 21M20 10H14M14 10V4M14 10L21 3M20 14V16.8C20 17.9201 20 18.4802 19.782 18.908C19.5903 19.2843 19.2843 19.5903 18.908 19.782C18.4802 20 17.9201 20 16.8 20H14M10 4H7.2C6.0799 4 5.51984 4 5.09202 4.21799C4.71569 4.40973 4.40973 4.71569 4.21799 5.09202C4 5.51984 4 6.07989 4 7.2V10" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 498 B

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="ai-text">
<path id="Vector" d="M2.33301 10.5H4.08301M2.33301 7H5.24967M2.33301 3.5H11.6663M9.91634 5.83333L10.7913 7.875L12.833 8.75L10.7913 9.625L9.91634 11.6667L9.04134 9.625L6.99967 8.75L9.04134 7.875L9.91634 5.83333Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 423 B

View File

@@ -0,0 +1,9 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="chat-bot">
<g id="Vector">
<path d="M13.0909 11.2727C14.0951 11.2727 14.9091 10.4587 14.9091 9.45455C14.9091 8.45039 14.0951 7.63636 13.0909 7.63636C12.0868 7.63636 11.2727 8.45039 11.2727 9.45455C11.2727 10.4587 12.0868 11.2727 13.0909 11.2727Z" fill="#D0D5DD"/>
<path d="M20.3636 22.1818H7.63636C5.62727 22.1818 4 23.8091 4 25.8182V40.3636C4 42.3727 5.62727 44 7.63636 44H33.0909C35.1 44 36.7273 42.3727 36.7273 40.3636V25.8182M13.0909 15.9998V11.2727M13.0909 11.2727C14.0951 11.2727 14.9091 10.4587 14.9091 9.45455C14.9091 8.45039 14.0951 7.63636 13.0909 7.63636C12.0868 7.63636 11.2727 8.45039 11.2727 9.45455C11.2727 10.4587 12.0868 11.2727 13.0909 11.2727ZM27.6364 5.81818C27.6364 4.81455 28.4509 4 29.4545 4H42.1818C43.1855 4 44 4.81455 44 5.81818V14.9091C44 15.9127 43.1855 16.7273 42.1818 16.7273H33.0909L27.6364 20.3636V5.81818Z" stroke="#D0D5DD" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<path id="Vector_2" d="M15.7275 30.364C15.7275 31.3179 14.9542 32.0913 14.0002 32.0913C13.0463 32.0913 12.2729 31.3179 12.2729 30.364C12.2729 29.41 13.0463 28.6367 14.0002 28.6367C14.9542 28.6367 15.7275 29.41 15.7275 30.364ZM28.4548 30.364C28.4548 31.3179 27.6814 32.0913 26.7275 32.0913C25.7735 32.0913 25.0002 31.3179 25.0002 30.364C25.0002 29.41 25.7735 28.6367 26.7275 28.6367C27.6814 28.6367 28.4548 29.41 28.4548 30.364Z" fill="#D0D5DD" stroke="#D0D5DD" stroke-width="2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,5 +1,14 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.1818 11.0909H3.81818C2.81364 11.0909 2 11.9045 2 12.9091V20.1818C2 21.1864 2.81364 22 3.81818 22H16.5455C17.55 22 18.3636 21.1864 18.3636 20.1818V12.9091M6.54545 7.99989V5.63636M6.54545 5.63636C7.04753 5.63636 7.45455 5.22935 7.45455 4.72727C7.45455 4.2252 7.04753 3.81818 6.54545 3.81818C6.04338 3.81818 5.63636 4.2252 5.63636 4.72727C5.63636 5.22935 6.04338 5.63636 6.54545 5.63636ZM13.8182 2.90909C13.8182 2.40727 14.2255 2 14.7273 2H21.0909C21.5927 2 22 2.40727 22 2.90909V7.45455C22 7.95636 21.5927 8.36364 21.0909 8.36364H16.5455L13.8182 10.1818V2.90909Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.00011 16.5454C7.75323 16.5454 8.36375 15.9349 8.36375 15.1818C8.36375 14.4286 7.75323 13.8181 7.00011 13.8181C6.247 13.8181 5.63647 14.4286 5.63647 15.1818C5.63647 15.9349 6.247 16.5454 7.00011 16.5454Z" fill="black"/>
<path d="M13.3637 16.5454C14.1169 16.5454 14.7274 15.9349 14.7274 15.1818C14.7274 14.4286 14.1169 13.8181 13.3637 13.8181C12.6106 13.8181 12.0001 14.4286 12.0001 15.1818C12.0001 15.9349 12.6106 16.5454 13.3637 16.5454Z" fill="black"/>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon" clip-path="url(#clip0_3167_27725)">
<path id="Vector" d="M5.93972 6.47002H2.2276C1.64161 6.47002 1.16699 6.94464 1.16699 7.53063V11.7731C1.16699 12.359 1.64161 12.8337 2.2276 12.8337H9.65184C10.2378 12.8337 10.7124 12.359 10.7124 11.7731V7.53063M3.81851 4.66693V3.2882M3.81851 3.2882C4.11139 3.2882 4.34881 3.05078 4.34881 2.7579C4.34881 2.46502 4.11139 2.2276 3.81851 2.2276C3.52563 2.2276 3.2882 2.46502 3.2882 2.7579C3.2882 3.05078 3.52563 3.2882 3.81851 3.2882ZM8.06093 1.6973C8.06093 1.40457 8.29851 1.16699 8.59123 1.16699H12.3034C12.5961 1.16699 12.8337 1.40457 12.8337 1.6973V4.34881C12.8337 4.64154 12.5961 4.87911 12.3034 4.87911H9.65184L8.06093 5.93972V1.6973Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<g id="Vector_2">
<path d="M4.08354 9.65146C4.52286 9.65146 4.87899 9.29532 4.87899 8.856C4.87899 8.41668 4.52286 8.06055 4.08354 8.06055C3.64422 8.06055 3.28809 8.41668 3.28809 8.856C3.28809 9.29532 3.64422 9.65146 4.08354 9.65146Z" fill="#344054"/>
<path d="M7.79566 9.65146C8.23498 9.65146 8.59112 9.29532 8.59112 8.856C8.59112 8.41668 8.23498 8.06055 7.79566 8.06055C7.35634 8.06055 7.00021 8.41668 7.00021 8.856C7.00021 9.29532 7.35634 9.65146 7.79566 9.65146Z" fill="#344054"/>
</g>
</g>
<defs>
<clipPath id="clip0_3167_27725">
<rect width="14" height="14" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,5 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="cute-robot">
<path id="Vector" d="M6.99967 2.33366H4.08301C3.43868 2.33366 2.91634 2.85599 2.91634 3.50033V6.41699C2.91634 7.06134 3.43868 7.58366 4.08301 7.58366H9.91634C10.5607 7.58366 11.083 7.06134 11.083 6.41699V3.50033C11.083 2.85599 10.5607 2.33366 9.91634 2.33366H6.99967ZM6.99967 2.33366V1.16699M3.49967 8.75033L2.33301 9.91699M3.49967 8.75033C3.49967 10.6833 5.06668 12.2503 6.99967 12.2503C8.93267 12.2503 10.4997 10.6833 10.4997 8.75033M3.49967 8.75033V7.58366M10.4997 8.75033L11.6663 9.91699M10.4997 8.75033V7.58366M5.24967 4.66699V5.25033M8.74967 4.66699V5.25033" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 779 B

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="bar-chart-square-02">
<path id="Icon" d="M5.33333 10V11.3333M8 7.33333V11.3333M10.6667 4.66667V11.3333M5.2 14H10.8C11.9201 14 12.4802 14 12.908 13.782C13.2843 13.5903 13.5903 13.2843 13.782 12.908C14 12.4802 14 11.9201 14 10.8V5.2C14 4.0799 14 3.51984 13.782 3.09202C13.5903 2.71569 13.2843 2.40973 12.908 2.21799C12.4802 2 11.9201 2 10.8 2H5.2C4.0799 2 3.51984 2 3.09202 2.21799C2.71569 2.40973 2.40973 2.71569 2.21799 3.09202C2 3.51984 2 4.0799 2 5.2V10.8C2 11.9201 2 12.4802 2.21799 12.908C2.40973 13.2843 2.71569 13.5903 3.09202 13.782C3.51984 14 4.0799 14 5.2 14Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 771 B

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="code-browser">
<path id="Icon" d="M22 9H2M14 17.5L16.5 15L14 12.5M10 12.5L7.5 15L10 17.5M2 7.8L2 16.2C2 17.8802 2 18.7202 2.32698 19.362C2.6146 19.9265 3.07354 20.3854 3.63803 20.673C4.27976 21 5.11984 21 6.8 21H17.2C18.8802 21 19.7202 21 20.362 20.673C20.9265 20.3854 21.3854 19.9265 21.673 19.362C22 18.7202 22 17.8802 22 16.2V7.8C22 6.11984 22 5.27977 21.673 4.63803C21.3854 4.07354 20.9265 3.6146 20.362 3.32698C19.7202 3 18.8802 3 17.2 3L6.8 3C5.11984 3 4.27976 3 3.63803 3.32698C3.07354 3.6146 2.6146 4.07354 2.32698 4.63803C2 5.27976 2 6.11984 2 7.8Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 755 B

View File

@@ -0,0 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="file-heart-02">
<path id="Icon" d="M13.5709 13.9883C13.5108 14.3786 13.175 14.6666 12.7802 14.6666H9.19984C8.90529 14.6666 8.6665 14.4279 8.6665 14.1333V12.2666C8.6665 11.9721 8.90529 11.7333 9.19984 11.7333H9.82654C9.93192 11.7333 10.0274 11.6713 10.0702 11.5749L11.0087 9.46348C11.0438 9.38432 11.1223 9.33331 11.2089 9.33331C11.5721 9.33331 11.8665 9.62771 11.8665 9.99087V10.9333C11.8665 11.0806 11.9859 11.2 12.1332 11.2H13.0673C13.5577 11.2 13.9326 11.637 13.858 12.1216L13.5709 13.9883Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector" d="M13.3332 6.66665V4.53331C13.3332 3.41321 13.3332 2.85316 13.1152 2.42533C12.9234 2.04901 12.6175 1.74305 12.2412 1.5513C11.8133 1.33331 11.2533 1.33331 10.1332 1.33331H5.8665C4.7464 1.33331 4.18635 1.33331 3.75852 1.5513C3.3822 1.74305 3.07624 2.04901 2.88449 2.42533C2.6665 2.85316 2.6665 3.41321 2.6665 4.53331V11.3333C2.6665 11.9533 2.6665 12.2633 2.73465 12.5176C2.91959 13.2078 3.45868 13.7469 4.14887 13.9318C4.4032 14 4.71319 14 5.33317 14M8.33317 7.33331H5.33317M5.99984 9.99998H5.33317M10.6665 4.66665H5.33317" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="git-branch-01">
<path id="Icon" d="M2 2V8.8C2 9.92011 2 10.4802 2.21799 10.908C2.40973 11.2843 2.71569 11.5903 3.09202 11.782C3.51984 12 4.0799 12 5.2 12H10M10 12C10 13.1046 10.8954 14 12 14C13.1046 14 14 13.1046 14 12C14 10.8954 13.1046 10 12 10C10.8954 10 10 10.8954 10 12ZM2 5.33333L10 5.33333M10 5.33333C10 6.4379 10.8954 7.33333 12 7.33333C13.1046 7.33333 14 6.4379 14 5.33333C14 4.22876 13.1046 3.33333 12 3.33333C10.8954 3.33333 10 4.22876 10 5.33333Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 661 B

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="prompt-engineering">
<path id="Icon" d="M14 6V5.2C14 4.0799 14 3.51984 13.782 3.09202C13.5903 2.7157 13.2843 2.40974 12.908 2.21799C12.4802 2 11.9201 2 10.8 2H5.2C4.0799 2 3.51984 2 3.09202 2.21799C2.7157 2.40973 2.40973 2.7157 2.21799 3.09202C2 3.51984 2 4.0799 2 5.2V10.8C2 11.9201 2 12.4802 2.21799 12.908C2.40973 13.2843 2.71569 13.5903 3.09202 13.782C3.51984 14 4.07989 14 5.2 14H6" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector" d="M4.6665 4.66669H4.67317M6.6665 4.66669H6.67317M8.6665 4.66669H8.67317" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Icon_2" d="M11.3333 8L11.5343 8.80399C11.7036 9.48123 11.7883 9.81985 11.9646 10.0954C12.1206 10.3391 12.3275 10.5461 12.5713 10.7021C12.8468 10.8784 13.1854 10.963 13.8627 11.1323L14.6667 11.3333L13.8627 11.5343C13.1854 11.7036 12.8468 11.7883 12.5713 11.9646C12.3275 12.1206 12.1206 12.3275 11.9646 12.5713C11.7883 12.8468 11.7036 13.1854 11.5343 13.8627L11.3333 14.6667L11.1323 13.8627C10.963 13.1854 10.8784 12.8468 10.7021 12.5713C10.5461 12.3275 10.3391 12.1206 10.0954 11.9646C9.81985 11.7883 9.48123 11.7036 8.80399 11.5343L8 11.3333L8.80399 11.1323C9.48123 10.963 9.81985 10.8784 10.0954 10.7021C10.3391 10.5461 10.5461 10.3391 10.7021 10.0954C10.8784 9.81985 10.963 9.48123 11.1323 8.80399L11.3333 8Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="terminal-square">
<path id="Icon" d="M7 15L10 12L7 9M13 15H17M7.8 21H16.2C17.8802 21 18.7202 21 19.362 20.673C19.9265 20.3854 20.3854 19.9265 20.673 19.362C21 18.7202 21 17.8802 21 16.2V7.8C21 6.11984 21 5.27976 20.673 4.63803C20.3854 4.07354 19.9265 3.6146 19.362 3.32698C18.7202 3 17.8802 3 16.2 3H7.8C6.11984 3 5.27976 3 4.63803 3.32698C4.07354 3.6146 3.6146 4.07354 3.32698 4.63803C3 5.27976 3 6.11984 3 7.8V16.2C3 17.8802 3 18.7202 3.32698 19.362C3.6146 19.9265 4.07354 20.3854 4.63803 20.673C5.27976 21 6.11984 21 7.8 21Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 725 B

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="align-left">
<path id="Icon" d="M16 10H3M20 6H3M20 14H3M16 18H3" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 261 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21 9.24995H12M21 3.99995L12 3.99995M21 14.75H3M21 20H3M4.28 2.95995L8.14667 5.85995C8.43616 6.07707 8.5809 6.18563 8.63266 6.31872C8.678 6.43529 8.678 6.56462 8.63266 6.68119C8.5809 6.81427 8.43616 6.92283 8.14667 7.13995L4.28 10.04C3.86802 10.3489 3.66203 10.5034 3.48961 10.4998C3.33956 10.4967 3.19885 10.4264 3.10632 10.3082C3 10.1724 3 9.91493 3 9.39995V3.59995C3 3.08498 3 2.82749 3.10632 2.6917C3.19885 2.57354 3.33956 2.50318 3.48961 2.50006C3.66203 2.49648 3.86802 2.65097 4.28 2.95995Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 691 B

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="letter-spacing-01">
<path id="Icon" d="M9 13L15 13M7 17L11.2717 7.60225C11.5031 7.09323 11.6188 6.83872 11.7791 6.75976C11.9184 6.69115 12.0816 6.69115 12.2209 6.75976C12.3812 6.83872 12.4969 7.09323 12.7283 7.60225L17 17M21 3V21M3 3L3 21" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 436 B

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="file-arrow-01">
<path id="Vector" d="M3.33333 12.333C3.33333 12.6426 3.33333 12.7974 3.35044 12.9274C3.4686 13.8249 4.17481 14.5311 5.07228 14.6492C5.20225 14.6663 5.35705 14.6663 5.66667 14.6663H10.8C11.9201 14.6663 12.4802 14.6663 12.908 14.4484C13.2843 14.2566 13.5903 13.9506 13.782 13.5743C14 13.1465 14 12.5864 14 11.4663V6.65849C14 6.16931 14 5.92472 13.9447 5.69454C13.8957 5.49047 13.8149 5.29538 13.7053 5.11644C13.5816 4.91461 13.4086 4.74165 13.0627 4.39575L10.9373 2.27027C10.5914 1.92436 10.4184 1.75141 10.2166 1.62773C10.0376 1.51807 9.84254 1.43726 9.63846 1.38827C9.40829 1.33301 9.1637 1.33301 8.67452 1.33301H5.66667C5.35705 1.33301 5.20225 1.33301 5.07228 1.35012C4.17481 1.46827 3.4686 2.17449 3.35044 3.07196M5.33333 5.99967L7.33333 7.99967M7.33333 7.99967L5.33333 9.99967M7.33333 7.99967H2" stroke="#475467" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1015 B

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="file-check-02">
<path id="Icon" d="M13.3337 8.33301V4.53301C13.3337 3.4129 13.3337 2.85285 13.1157 2.42503C12.9239 2.0487 12.618 1.74274 12.2416 1.55099C11.8138 1.33301 11.2538 1.33301 10.1337 1.33301H5.86699C4.74689 1.33301 4.18683 1.33301 3.75901 1.55099C3.38269 1.74274 3.07673 2.0487 2.88498 2.42503C2.66699 2.85285 2.66699 3.4129 2.66699 4.53301V11.4663C2.66699 12.5864 2.66699 13.1465 2.88498 13.5743C3.07673 13.9506 3.38269 14.2566 3.75901 14.4484C4.18683 14.6663 4.74689 14.6663 5.86699 14.6663H8.00033M9.33366 7.33301H5.33366M6.66699 9.99967H5.33366M10.667 4.66634H5.33366M9.66699 12.6663L11.0003 13.9997L14.0003 10.9997" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 832 B

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="file-plus-01">
<path id="Icon" d="M13.3332 6.99967V4.53301C13.3332 3.4129 13.3332 2.85285 13.1152 2.42503C12.9234 2.0487 12.6175 1.74274 12.2412 1.55099C11.8133 1.33301 11.2533 1.33301 10.1332 1.33301H5.8665C4.7464 1.33301 4.18635 1.33301 3.75852 1.55099C3.3822 1.74274 3.07624 2.0487 2.88449 2.42503C2.6665 2.85285 2.6665 3.4129 2.6665 4.53301V11.4663C2.6665 12.5864 2.6665 13.1465 2.88449 13.5743C3.07624 13.9506 3.3822 14.2566 3.75852 14.4484C4.18635 14.6663 4.7464 14.6663 5.8665 14.6663H7.99984M11.9998 13.9997V9.99967M9.99984 11.9997H13.9998" stroke="#475467" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 749 B

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="file-text">
<path id="Icon" d="M14 2H6C5.46957 2 4.96086 2.21071 4.58579 2.58579C4.21071 2.96086 4 3.46957 4 4V20C4 20.5304 4.21071 21.0391 4.58579 21.4142C4.96086 21.7893 5.46957 22 6 22H18C18.5304 22 19.0391 21.7893 19.4142 21.4142C19.7893 21.0391 20 20.5304 20 20V8M14 2L20 8M14 2V8H20M16 13H8M16 17H8M10 9H8" stroke="#101828" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 511 B

View File

@@ -0,0 +1,10 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="check-circle" clip-path="url(#clip0_465_21765)">
<path id="Icon" d="M4.37533 6.99984L6.12533 8.74984L9.62533 5.24984M12.8337 6.99984C12.8337 10.2215 10.222 12.8332 7.00033 12.8332C3.77866 12.8332 1.16699 10.2215 1.16699 6.99984C1.16699 3.77818 3.77866 1.1665 7.00033 1.1665C10.222 1.1665 12.8337 3.77818 12.8337 6.99984Z" stroke="#12B76A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_465_21765">
<rect width="14" height="14" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 625 B

View File

@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="check-done-01">
<path id="Icon" d="M6 15L8 17L12.5 12.5M8 8V5.2C8 4.0799 8 3.51984 8.21799 3.09202C8.40973 2.71569 8.71569 2.40973 9.09202 2.21799C9.51984 2 10.0799 2 11.2 2H18.8C19.9201 2 20.4802 2 20.908 2.21799C21.2843 2.40973 21.5903 2.71569 21.782 3.09202C22 3.51984 22 4.0799 22 5.2V12.8C22 13.9201 22 14.4802 21.782 14.908C21.5903 15.2843 21.2843 15.5903 20.908 15.782C20.4802 16 19.9201 16 18.8 16H16M5.2 22H12.8C13.9201 22 14.4802 22 14.908 21.782C15.2843 21.5903 15.5903 21.2843 15.782 20.908C16 20.4802 16 19.9201 16 18.8V11.2C16 10.0799 16 9.51984 15.782 9.09202C15.5903 8.71569 15.2843 8.40973 14.908 8.21799C14.4802 8 13.9201 8 12.8 8H5.2C4.0799 8 3.51984 8 3.09202 8.21799C2.71569 8.40973 2.40973 8.71569 2.21799 9.09202C2 9.51984 2 10.0799 2 11.2V18.8C2 19.9201 2 20.4802 2.21799 20.908C2.40973 21.2843 2.71569 21.5903 3.09202 21.782C3.51984 22 4.07989 22 5.2 22Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="checklist-square">
<path id="Vector" d="M9.7823 11.9146C9.32278 11.6082 8.70191 11.7324 8.39554 12.1919C8.08918 12.6514 8.21333 13.2723 8.67285 13.5787L9.7823 11.9146ZM10.9151 13.8717L10.3603 14.7037C10.8019 14.9982 11.3966 14.8963 11.7151 14.4717L10.9151 13.8717ZM14.5226 10.7284C14.8539 10.2865 14.7644 9.65973 14.3225 9.32836C13.8807 8.99699 13.2539 9.08653 12.9225 9.52836L14.5226 10.7284ZM19.3333 11C18.781 11 18.3333 11.4477 18.3333 12C18.3333 12.5523 18.781 13 19.3333 13V11ZM22 13C22.5523 13 23 12.5523 23 12C23 11.4477 22.5523 11 22 11V13ZM19.3333 19C18.781 19 18.3333 19.4477 18.3333 20C18.3333 20.5523 18.781 21 19.3333 21V19ZM22 21C22.5523 21 23 20.5523 23 20C23 19.4477 22.5523 19 22 19V21ZM9.86913 19.9163C9.4096 19.6099 8.78873 19.7341 8.48238 20.1937C8.17602 20.6532 8.3002 21.274 8.75973 21.5804L9.86913 19.9163ZM11.0019 21.8734L10.4472 22.7054C10.8888 22.9998 11.4835 22.8979 11.8019 22.4734L11.0019 21.8734ZM14.6094 18.7301C14.9408 18.2883 14.8512 17.6615 14.4094 17.3301C13.9676 16.9987 13.3408 17.0883 13.0094 17.5301L14.6094 18.7301ZM6.18404 27.564L5.73005 28.455H5.73005L6.18404 27.564ZM4.43597 25.816L3.54497 26.27H3.54497L4.43597 25.816ZM27.564 25.816L28.455 26.27L27.564 25.816ZM25.816 27.564L26.27 28.455L25.816 27.564ZM25.816 4.43597L26.27 3.54497V3.54497L25.816 4.43597ZM27.564 6.18404L28.455 5.73005V5.73005L27.564 6.18404ZM6.18404 4.43597L5.73005 3.54497L6.18404 4.43597ZM4.43597 6.18404L3.54497 5.73005L4.43597 6.18404ZM8.67285 13.5787L10.3603 14.7037L11.4698 13.0397L9.7823 11.9146L8.67285 13.5787ZM11.7151 14.4717L14.5226 10.7284L12.9225 9.52836L10.1151 13.2717L11.7151 14.4717ZM19.3333 13H22V11H19.3333V13ZM19.3333 21H22V19H19.3333V21ZM8.75973 21.5804L10.4472 22.7054L11.5566 21.0413L9.86913 19.9163L8.75973 21.5804ZM11.8019 22.4734L14.6094 18.7301L13.0094 17.5301L10.2019 21.2733L11.8019 22.4734ZM10.4 5H21.6V3H10.4V5ZM27 10.4V21.6H29V10.4H27ZM21.6 27H10.4V29H21.6V27ZM5 21.6V10.4H3V21.6H5ZM10.4 27C9.26339 27 8.47108 26.9992 7.85424 26.9488C7.24907 26.8994 6.90138 26.8072 6.63803 26.673L5.73005 28.455C6.32234 28.7568 6.96253 28.8826 7.69138 28.9422C8.40855 29.0008 9.2964 29 10.4 29V27ZM3 21.6C3 22.7036 2.99922 23.5914 3.05782 24.3086C3.11737 25.0375 3.24318 25.6777 3.54497 26.27L5.32698 25.362C5.19279 25.0986 5.10062 24.7509 5.05118 24.1458C5.00078 23.5289 5 22.7366 5 21.6H3ZM6.63803 26.673C6.07354 26.3854 5.6146 25.9265 5.32698 25.362L3.54497 26.27C4.02433 27.2108 4.78924 27.9757 5.73005 28.455L6.63803 26.673ZM27 21.6C27 22.7366 26.9992 23.5289 26.9488 24.1458C26.8994 24.7509 26.8072 25.0986 26.673 25.362L28.455 26.27C28.7568 25.6777 28.8826 25.0375 28.9422 24.3086C29.0008 23.5914 29 22.7036 29 21.6H27ZM21.6 29C22.7036 29 23.5914 29.0008 24.3086 28.9422C25.0375 28.8826 25.6777 28.7568 26.27 28.455L25.362 26.673C25.0986 26.8072 24.7509 26.8994 24.1458 26.9488C23.5289 26.9992 22.7366 27 21.6 27V29ZM26.673 25.362C26.3854 25.9265 25.9265 26.3854 25.362 26.673L26.27 28.455C27.2108 27.9757 27.9757 27.2108 28.455 26.27L26.673 25.362ZM21.6 5C22.7366 5 23.5289 5.00078 24.1458 5.05118C24.7509 5.10062 25.0986 5.19279 25.362 5.32698L26.27 3.54497C25.6777 3.24318 25.0375 3.11737 24.3086 3.05782C23.5914 2.99922 22.7036 3 21.6 3V5ZM29 10.4C29 9.2964 29.0008 8.40855 28.9422 7.69138C28.8826 6.96253 28.7568 6.32234 28.455 5.73005L26.673 6.63803C26.8072 6.90138 26.8994 7.24907 26.9488 7.85424C26.9992 8.47108 27 9.26339 27 10.4H29ZM25.362 5.32698C25.9265 5.6146 26.3854 6.07354 26.673 6.63803L28.455 5.73005C27.9757 4.78924 27.2108 4.02433 26.27 3.54497L25.362 5.32698ZM10.4 3C9.2964 3 8.40855 2.99922 7.69138 3.05782C6.96253 3.11737 6.32234 3.24318 5.73005 3.54497L6.63803 5.32698C6.90138 5.19279 7.24907 5.10062 7.85424 5.05118C8.47108 5.00078 9.26339 5 10.4 5V3ZM5 10.4C5 9.26339 5.00078 8.47108 5.05118 7.85424C5.10062 7.24907 5.19279 6.90138 5.32698 6.63803L3.54497 5.73005C3.24318 6.32234 3.11737 6.96253 3.05782 7.69138C2.99922 8.40855 3 9.2964 3 10.4H5ZM5.73005 3.54497C4.78924 4.02433 4.02433 4.78924 3.54497 5.73005L5.32698 6.63803C5.6146 6.07354 6.07354 5.6146 6.63803 5.32698L5.73005 3.54497Z" fill="#D0D5DD"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon">
<path id="Vector" d="M8.75 11H14M8.75 5H14M2 5.75L3.125 6.5L5.375 3.5M2 11.75L3.125 12.5L5.375 9.5" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -0,0 +1,15 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon">
<g id="Solid">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.83333 2.91667C5.83333 2.27233 6.35567 1.75 7 1.75C7.64433 1.75 8.16667 2.27233 8.16667 2.91667C8.16667 3.561 7.64433 4.08333 7 4.08333C6.35567 4.08333 5.83333 3.561 5.83333 2.91667Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.83333 7C5.83333 6.35567 6.35567 5.83333 7 5.83333C7.64433 5.83333 8.16667 6.35567 8.16667 7C8.16667 7.64433 7.64433 8.16667 7 8.16667C6.35567 8.16667 5.83333 7.64433 5.83333 7Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.83333 11.0833C5.83333 10.439 6.35567 9.91667 7 9.91667C7.64433 9.91667 8.16667 10.439 8.16667 11.0833C8.16667 11.7277 7.64433 12.25 7 12.25C6.35567 12.25 5.83333 11.7277 5.83333 11.0833Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.91667 2.91667C9.91667 2.27233 10.439 1.75 11.0833 1.75C11.7277 1.75 12.25 2.27233 12.25 2.91667C12.25 3.561 11.7277 4.08333 11.0833 4.08333C10.439 4.08333 9.91667 3.561 9.91667 2.91667Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.91667 7C9.91667 6.35567 10.439 5.83333 11.0833 5.83333C11.7277 5.83333 12.25 6.35567 12.25 7C12.25 7.64433 11.7277 8.16667 11.0833 8.16667C10.439 8.16667 9.91667 7.64433 9.91667 7Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.91667 11.0833C9.91667 10.439 10.439 9.91667 11.0833 9.91667C11.7277 9.91667 12.25 10.439 12.25 11.0833C12.25 11.7277 11.7277 12.25 11.0833 12.25C10.439 12.25 9.91667 11.7277 9.91667 11.0833Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.75 2.91667C1.75 2.27233 2.27233 1.75 2.91667 1.75C3.561 1.75 4.08333 2.27233 4.08333 2.91667C4.08333 3.561 3.561 4.08333 2.91667 4.08333C2.27233 4.08333 1.75 3.561 1.75 2.91667Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.75 7C1.75 6.35567 2.27233 5.83333 2.91667 5.83333C3.561 5.83333 4.08333 6.35567 4.08333 7C4.08333 7.64433 3.561 8.16667 2.91667 8.16667C2.27233 8.16667 1.75 7.64433 1.75 7Z" fill="#155EEF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.75 11.0833C1.75 10.439 2.27233 9.91667 2.91667 9.91667C3.561 9.91667 4.08333 10.439 4.08333 11.0833C4.08333 11.7277 3.561 12.25 2.91667 12.25C2.27233 12.25 1.75 11.7277 1.75 11.0833Z" fill="#155EEF"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="log-in-04">
<g id="Solid">
<path d="M8.00016 1.99984C5.78015 1.99984 3.84088 3.20518 2.80244 5.00032C2.61808 5.31903 2.21026 5.42794 1.89155 5.24357C1.57285 5.05921 1.46394 4.65139 1.6483 4.33269C2.91526 2.14249 5.28495 0.666504 8.00016 0.666504C12.0502 0.666504 15.3335 3.94975 15.3335 7.99984C15.3335 12.0499 12.0502 15.3332 8.00016 15.3332C5.28495 15.3332 2.91526 13.8572 1.6483 11.667C1.46394 11.3483 1.57285 10.9405 1.89155 10.7561C2.21026 10.5717 2.61808 10.6806 2.80244 10.9994C3.84088 12.7945 5.78015 13.9998 8.00016 13.9998C11.3139 13.9998 14.0002 11.3135 14.0002 7.99984C14.0002 4.68613 11.3139 1.99984 8.00016 1.99984Z" fill="#344054"/>
<path d="M7.52876 4.86177C7.78911 4.60142 8.21122 4.60142 8.47157 4.86177L11.1382 7.52843C11.3986 7.78878 11.3986 8.21089 11.1382 8.47124L8.47157 11.1379C8.21122 11.3983 7.78911 11.3983 7.52876 11.1379C7.26841 10.8776 7.26841 10.4554 7.52876 10.1951L9.05735 8.6665H2.00016C1.63197 8.6665 1.3335 8.36803 1.3335 7.99984C1.3335 7.63165 1.63197 7.33317 2.00016 7.33317H9.05735L7.52876 5.80457C7.26841 5.54423 7.26841 5.12212 7.52876 4.86177Z" fill="#344054"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="log-out-04">
<g id="Solid">
<path d="M0.666504 8.00016C0.666504 4.3422 3.52829 1.3335 7.11095 1.3335C8.28872 1.3335 9.3935 1.66091 10.3431 2.23137C10.6588 2.42097 10.7609 2.83053 10.5713 3.14615C10.3817 3.46177 9.97216 3.56394 9.65654 3.37434C8.90651 2.92378 8.03794 2.66683 7.11095 2.66683C4.31165 2.66683 1.99984 5.03071 1.99984 8.00016C1.99984 10.9696 4.31165 13.3335 7.11095 13.3335C8.03794 13.3335 8.90651 13.0765 9.65654 12.626C9.97216 12.4364 10.3817 12.5386 10.5713 12.8542C10.7609 13.1698 10.6588 13.5794 10.3431 13.769C9.3935 14.3394 8.28872 14.6668 7.11095 14.6668C3.52829 14.6668 0.666504 11.6581 0.666504 8.00016Z" fill="#98A2B3"/>
<path d="M11.5284 4.86209C11.7888 4.60174 12.2109 4.60174 12.4712 4.86209L15.1379 7.52876C15.3983 7.78911 15.3983 8.21122 15.1379 8.47157L12.4712 11.1382C12.2109 11.3986 11.7888 11.3986 11.5284 11.1382C11.2681 10.8779 11.2681 10.4558 11.5284 10.1954L13.057 8.66683H5.99984C5.63165 8.66683 5.33317 8.36835 5.33317 8.00016C5.33317 7.63197 5.63165 7.3335 5.99984 7.3335H13.057L11.5284 5.8049C11.2681 5.54455 11.2681 5.12244 11.5284 4.86209Z" fill="#98A2B3"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,5 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="plus">
<path id="Icon" d="M5.00004 2.08325V7.91659M2.08337 4.99992H7.91671" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 274 B

View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.4" d="M4 16.2422C2.79401 15.435 2 14.0602 2 12.5C2 10.1564 3.79151 8.23129 6.07974 8.01937C6.54781 5.17213 9.02024 3 12 3C14.9798 3 17.4522 5.17213 17.9203 8.01937C20.2085 8.23129 22 10.1564 22 12.5C22 14.0602 21.206 15.435 20 16.2422" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 16L12 12M12 12L16 16M12 12L12 21" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 564 B

View File

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 9H21M3 15H21M12 3V21M7.8 3H16.2C17.8802 3 18.7202 3 19.362 3.32698C19.9265 3.6146 20.3854 4.07354 20.673 4.63803C21 5.27976 21 6.11984 21 7.8V16.2C21 17.8802 21 18.7202 20.673 19.362C20.3854 19.9265 19.9265 20.3854 19.362 20.673C18.7202 21 17.8802 21 16.2 21H7.8C6.11984 21 5.27976 21 4.63803 20.673C4.07354 20.3854 3.6146 19.9265 3.32698 19.362C3 18.7202 3 17.8802 3 16.2V7.8C3 6.11984 3 5.27976 3.32698 4.63803C3.6146 4.07354 4.07354 3.6146 4.63803 3.32698C5.27976 3 6.11984 3 7.8 3Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon">
<g id="Vector">
<path d="M9.33366 10.667C9.33366 9.93061 9.93061 9.33366 10.667 9.33366H12.0003C12.7367 9.33366 13.3337 9.93061 13.3337 10.667V12.0003C13.3337 12.7367 12.7367 13.3337 12.0003 13.3337H10.667C9.93061 13.3337 9.33366 12.7367 9.33366 12.0003V10.667Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.66699 10.667C2.66699 9.93059 3.26395 9.33366 4.00033 9.33366H5.33366C6.07004 9.33366 6.66699 9.93059 6.66699 10.667V12.0003C6.66699 12.7367 6.07004 13.3337 5.33366 13.3337H4.00033C3.26395 13.3337 2.66699 12.7367 2.66699 12.0003V10.667Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2.66699 4.00033C2.66699 3.26395 3.26393 2.66699 4.00033 2.66699H5.33366C6.07006 2.66699 6.66699 3.26395 6.66699 4.00033V5.33366C6.66699 6.07004 6.07006 6.66699 5.33366 6.66699H4.00033C3.26393 6.66699 2.66699 6.07004 2.66699 5.33366V4.00033Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<path id="Vector_2" d="M11.6409 2.1899C11.5143 1.93674 11.153 1.93674 11.0265 2.1899L10.3544 3.53389C10.3213 3.60035 10.2673 3.65425 10.2008 3.68748L8.85684 4.35948C8.60371 4.48606 8.60371 4.84732 8.85684 4.97389L10.2008 5.64589C10.2673 5.67913 10.3213 5.73303 10.3544 5.7995L11.0265 7.14348C11.153 7.39664 11.5143 7.39664 11.6409 7.14348L12.3129 5.7995C12.3461 5.73303 12.4 5.67913 12.4665 5.64589L13.8105 4.97389C14.0636 4.84732 14.0636 4.48606 13.8105 4.35948L12.4665 3.68748C12.4 3.65425 12.3461 3.60035 12.3129 3.53389L11.6409 2.1899Z" fill="#667085"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,10 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="route" clip-path="url(#clip0_3167_28693)">
<path id="Icon" d="M6.70866 2.91699H6.96206C8.73962 2.91699 9.6284 2.91699 9.96578 3.23624C10.2574 3.51221 10.3867 3.91874 10.3079 4.31245C10.2168 4.76792 9.49122 5.28116 8.03999 6.30763L5.66899 7.98468C4.21777 9.01116 3.49215 9.5244 3.40106 9.97987C3.32233 10.3736 3.45157 10.7801 3.7432 11.0561C4.08059 11.3753 4.96937 11.3753 6.74693 11.3753H7.29199M4.66699 2.91699C4.66699 3.88349 3.88349 4.66699 2.91699 4.66699C1.95049 4.66699 1.16699 3.88349 1.16699 2.91699C1.16699 1.95049 1.95049 1.16699 2.91699 1.16699C3.88349 1.16699 4.66699 1.95049 4.66699 2.91699ZM12.8337 11.0837C12.8337 12.0502 12.0502 12.8337 11.0837 12.8337C10.1172 12.8337 9.33366 12.0502 9.33366 11.0837C9.33366 10.1172 10.1172 9.33366 11.0837 9.33366C12.0502 9.33366 12.8337 10.1172 12.8337 11.0837Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_3167_28693">
<rect width="14" height="14" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,13 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="play-circle" clip-path="url(#clip0_3607_26538)">
<g id="Icon">
<path d="M7.99992 14.6666C11.6818 14.6666 14.6666 11.6819 14.6666 7.99998C14.6666 4.31808 11.6818 1.33331 7.99992 1.33331C4.31802 1.33331 1.33325 4.31808 1.33325 7.99998C1.33325 11.6819 4.31802 14.6666 7.99992 14.6666Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.66659 5.33331L10.6666 7.99998L6.66659 10.6666V5.33331Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
<defs>
<clipPath id="clip0_3607_26538">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 747 B

View File

@@ -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="Icon">
<path id="Icon_2" d="M2.5 2.49482C2.5 2.00924 2.5 1.76644 2.60125 1.63261C2.68945 1.51601 2.82426 1.44386 2.9702 1.43515C3.13772 1.42515 3.33973 1.55982 3.74376 1.82918L9.00154 5.33436C9.33538 5.55693 9.5023 5.66821 9.56047 5.80847C9.61133 5.9311 9.61133 6.06891 9.56047 6.19154C9.5023 6.3318 9.33538 6.44308 9.00154 6.66564L3.74376 10.1708C3.33973 10.4402 3.13772 10.5749 2.9702 10.5649C2.82426 10.5561 2.68945 10.484 2.60125 10.3674C2.5 10.2336 2.5 9.99077 2.5 9.50519V2.49482Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 689 B

View File

@@ -0,0 +1,8 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon">
<g id="Icon_2">
<path d="M8.49967 14.6663C12.1816 14.6663 15.1663 11.6816 15.1663 7.99967C15.1663 4.31778 12.1816 1.33301 8.49967 1.33301C4.81778 1.33301 1.83301 4.31778 1.83301 7.99967C1.83301 11.6816 4.81778 14.6663 8.49967 14.6663Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.4997 5.99967H6.49967V9.99967H10.4997V5.99967Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 593 B

View File

@@ -0,0 +1,10 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon" clip-path="url(#clip0_467_1645)">
<path id="Icon_2" d="M1.5 3.9C1.5 3.05992 1.5 2.63988 1.66349 2.31901C1.8073 2.03677 2.03677 1.8073 2.31901 1.66349C2.63988 1.5 3.05992 1.5 3.9 1.5H8.1C8.94008 1.5 9.36012 1.5 9.68099 1.66349C9.96323 1.8073 10.1927 2.03677 10.3365 2.31901C10.5 2.63988 10.5 3.05992 10.5 3.9V8.1C10.5 8.94008 10.5 9.36012 10.3365 9.68099C10.1927 9.96323 9.96323 10.1927 9.68099 10.3365C9.36012 10.5 8.94008 10.5 8.1 10.5H3.9C3.05992 10.5 2.63988 10.5 2.31901 10.3365C2.03677 10.1927 1.8073 9.96323 1.66349 9.68099C1.5 9.36012 1.5 8.94008 1.5 8.1V3.9Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_467_1645">
<rect width="12" height="12" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 877 B

View File

@@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon">
<path id="Vector" d="M29.2673 17.3332C29.3109 16.8946 29.3332 16.4498 29.3332 15.9998C29.3332 8.63604 23.3636 2.6665 15.9998 2.6665C8.63604 2.6665 2.6665 8.63604 2.6665 15.9998C2.6665 23.3636 8.63604 29.3332 15.9998 29.3332C16.2234 29.3332 16.4457 29.3277 16.6665 29.3168C16.8902 29.3058 17.1125 29.2892 17.3332 29.2673M15.9998 7.99984V15.9998L10.8458 18.5102M23.7065 19.9464L28.8621 23.8131C29.2481 24.1026 29.441 24.2473 29.5101 24.4248C29.5705 24.5802 29.5705 24.7527 29.5101 24.9081C29.441 25.0855 29.2481 25.2303 28.8621 25.5198L23.7065 29.3864C23.1572 29.7984 22.8825 30.0044 22.6526 29.9996C22.4526 29.9955 22.265 29.9017 22.1416 29.7441C21.9998 29.5631 21.9998 29.2197 21.9998 28.5331V20.7998C21.9998 20.1131 21.9998 19.7698 22.1416 19.5888C22.265 19.4312 22.4526 19.3374 22.6526 19.3333C22.8825 19.3285 23.1572 19.5345 23.7065 19.9464Z" stroke="#D0D5DD" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon" clip-path="url(#clip0_635_10941)">
<path id="Vector" d="M14.6334 8.66683C14.6552 8.44756 14.6663 8.22516 14.6663 8.00016C14.6663 4.31826 11.6816 1.3335 7.99967 1.3335C4.31778 1.3335 1.33301 4.31826 1.33301 8.00016C1.33301 11.6821 4.31778 14.6668 7.99967 14.6668C8.11145 14.6668 8.22258 14.6641 8.33301 14.6586C8.44487 14.6531 8.556 14.6449 8.66634 14.6339M7.99967 4.00016V8.00016L5.42265 9.25534M11.853 9.97346L14.4308 11.9068C14.6238 12.0515 14.7203 12.1239 14.7548 12.2126C14.785 12.2904 14.785 12.3766 14.7548 12.4543C14.7203 12.543 14.6238 12.6154 14.4308 12.7601L11.853 14.6935C11.5784 14.8995 11.441 15.0024 11.3261 15.0001C11.226 14.998 11.1322 14.9511 11.0706 14.8723C10.9997 14.7818 10.9997 14.6101 10.9997 14.2668V10.4001C10.9997 10.0568 10.9997 9.88516 11.0706 9.79463C11.1322 9.71585 11.226 9.66895 11.3261 9.66687C11.441 9.66448 11.5784 9.76747 11.853 9.97346Z" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_635_10941">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,9 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="clock-refresh">
<g id="Solid">
<path d="M9.76984 2.8375L9.71551 3.04027C8.27602 1.22839 5.68891 0.69459 3.62457 1.88644C2.25681 2.67611 1.43067 4.04369 1.27558 5.50073C1.24636 5.77532 1.44526 6.02161 1.71985 6.05084C1.99444 6.08007 2.24074 5.88116 2.26997 5.60657C2.39268 4.4537 3.04533 3.37556 4.12456 2.75247C5.7025 1.84145 7.66731 2.20754 8.82211 3.53002L8.65016 3.48395C8.38343 3.41248 8.10926 3.57077 8.03779 3.8375C7.96632 4.10424 8.12461 4.37841 8.39134 4.44988L9.75737 4.8159C10.0241 4.88737 10.2983 4.72908 10.3697 4.46235L10.7358 3.09632C10.8072 2.82959 10.6489 2.55542 10.3822 2.48395C10.1155 2.41248 9.84131 2.57077 9.76984 2.8375Z" fill="#667085"/>
<path d="M10.2792 5.94921C10.5538 5.97844 10.7527 6.22473 10.7235 6.49932C10.5684 7.95635 9.74225 9.32394 8.3745 10.1136C6.31011 11.3055 3.72295 10.7716 2.28347 8.95968L2.22918 9.1623C2.15771 9.42903 1.88354 9.58732 1.61681 9.51585C1.35008 9.44438 1.19178 9.17021 1.26325 8.90348L1.62928 7.53746C1.70075 7.27072 1.97492 7.11243 2.24165 7.1839L3.60768 7.54993C3.87441 7.6214 4.0327 7.89557 3.96123 8.1623C3.88976 8.42903 3.61559 8.58732 3.34886 8.51585L3.17668 8.46972C4.33144 9.79246 6.29644 10.1587 7.8745 9.24758C8.95373 8.62449 9.60638 7.54634 9.72909 6.39348C9.75832 6.11889 10.0046 5.91998 10.2792 5.94921Z" fill="#667085"/>
<path d="M6.49954 3.74997C6.49954 3.47382 6.27568 3.24997 5.99954 3.24997C5.7234 3.24997 5.49954 3.47382 5.49954 3.74997V5.99997C5.49954 6.1756 5.59169 6.33835 5.74229 6.42871L6.99229 7.17871C7.22908 7.32079 7.53621 7.244 7.67828 7.00721C7.82036 6.77042 7.74358 6.46329 7.50679 6.32122L6.49954 5.71687V3.74997Z" fill="#667085"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,5 @@
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon">
<path id="Icon_2" d="M11.6667 8.66667V10.3C11.6667 10.9534 11.6667 11.2801 11.5395 11.5297C11.4277 11.7492 11.2492 11.9277 11.0297 12.0395C10.7801 12.1667 10.4534 12.1667 9.8 12.1667H8.16667M5.83333 2.83333H4.2C3.54661 2.83333 3.21991 2.83333 2.97034 2.96049C2.75082 3.07234 2.57234 3.25082 2.46049 3.47034C2.33333 3.71991 2.33333 4.04661 2.33333 4.7V6.33333M8.75 5.75L12.25 2.25M12.25 2.25H8.75M12.25 2.25V5.75M5.25 9.25L1.75 12.75M1.75 12.75H5.25M1.75 12.75L1.75 9.25" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 679 B

View File

@@ -0,0 +1,7 @@
<svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="chat-bot">
<path id="Vector" d="M4.20913 2.76912L4.09542 2.83543L3.98172 2.76912C3.90566 2.72476 3.86328 2.64979 3.86328 2.57101C3.86328 2.44347 3.96789 2.33887 4.09542 2.33887C4.22296 2.33887 4.32757 2.44347 4.32757 2.57101C4.32757 2.64979 4.28519 2.72476 4.20913 2.76912Z" fill="#1570EF" stroke="#1570EF" stroke-width="1.25"/>
<path id="Vector_2" d="M10.0174 6.00058C10.0123 5.98686 10.0097 5.97229 10.0046 5.95858C9.81684 5.48158 9.35398 5.14258 8.81056 5.14258H8.66784L7.52484 5.99972C7.33713 6.14029 7.11556 6.21444 6.88284 6.21444C6.29184 6.21444 5.81056 5.73358 5.81056 5.14258H2.81013C2.10127 5.14258 1.52441 5.71944 1.52441 6.42829V9.85686C1.52441 10.5657 2.10127 11.1426 2.81013 11.1426H8.81013C9.51899 11.1426 10.0958 10.5657 10.0958 9.85686V6.42829C10.0958 6.34386 10.0868 6.26158 10.071 6.18186C10.0586 6.11886 10.0384 6.05972 10.0174 6.00058ZM3.88156 8.57115C3.52713 8.57115 3.2387 8.28272 3.2387 7.92829C3.2387 7.57386 3.52713 7.28544 3.88156 7.28544C4.23599 7.28544 4.52441 7.57386 4.52441 7.92829C4.52441 8.28272 4.23599 8.57115 3.88156 8.57115ZM7.7387 8.57115C7.38427 8.57115 7.09584 8.28272 7.09584 7.92829C7.09584 7.57386 7.38427 7.28544 7.7387 7.28544C8.09313 7.28544 8.38156 7.57386 8.38156 7.92829C8.38156 8.28272 8.09313 8.57115 7.7387 8.57115Z" fill="#1570EF"/>
<path id="Vector_3" d="M6.66699 5.14314V1.71456C6.66699 1.24099 7.05056 0.857422 7.52413 0.857422H10.9527C11.4263 0.857422 11.8098 1.24099 11.8098 1.71456V3.42885C11.8098 3.90242 11.4263 4.28599 10.9527 4.28599H8.38128L7.00985 5.31456C6.86842 5.42042 6.66699 5.31971 6.66699 5.14314Z" fill="#1570EF"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.00195 4C3.00195 3.44772 3.44967 3 4.00195 3H20.002C20.5542 3 21.002 3.44772 21.002 4C21.002 4.55228 20.5542 5 20.002 5H4.00195C3.44967 5 3.00195 4.55228 3.00195 4Z" fill="black"/>
<path d="M3.00195 8C3.00195 7.44772 3.44967 7 4.00195 7H10.502C11.0542 7 11.502 7.44772 11.502 8C11.502 8.55228 11.0542 9 10.502 9H4.00195C3.44967 9 3.00195 8.55228 3.00195 8Z" fill="black"/>
<path d="M4 11C3.44772 11 3 11.4477 3 12C3 12.5523 3.44772 13 4 13H7.0022C7.55448 13 8.0022 12.5523 8.0022 12C8.0022 11.4477 7.55448 11 7.0022 11H4Z" fill="black"/>
<path d="M19.2584 8.70705C18.0868 7.53548 16.1873 7.53547 15.0158 8.70705L7.29485 16.428C7.10731 16.6155 7.00195 16.8699 7.00195 17.1351V20.9999C7.00195 21.5522 7.44967 21.9999 8.00195 21.9999H11.8668C12.132 21.9999 12.3864 21.8946 12.5739 21.7071L20.2948 13.9861C21.4664 12.8146 21.4664 10.9151 20.2948 9.74349L19.2584 8.70705Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 997 B

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="message-smile-square">
<path id="Solid" fill-rule="evenodd" clip-rule="evenodd" d="M10.8273 1.33337H5.17221C4.63556 1.33337 4.19265 1.33336 3.83185 1.36284C3.45712 1.39345 3.11265 1.45916 2.7891 1.62402C2.28733 1.87969 1.87938 2.28763 1.62372 2.7894C1.45886 3.11296 1.39315 3.45743 1.36253 3.83216C1.33306 4.19295 1.33306 4.63586 1.33307 5.17251L1.33304 9.42509C1.33275 9.95535 1.33254 10.3491 1.42394 10.6902C1.67052 11.6105 2.38931 12.3293 3.30955 12.5758C3.51453 12.6308 3.73853 12.6526 3.99974 12.6612L3.99974 13.5807C3.99971 13.7375 3.99967 13.8974 4.01157 14.0284C4.02296 14.1537 4.05309 14.3902 4.2182 14.597C4.40818 14.835 4.69628 14.9735 5.00082 14.9732C5.26547 14.9729 5.46897 14.8487 5.57392 14.7793C5.68366 14.7067 5.80847 14.6068 5.93093 14.5088L7.53968 13.2218C7.8854 12.9453 7.98804 12.8672 8.0947 12.8127C8.20168 12.758 8.31556 12.7181 8.43324 12.6939C8.55057 12.6699 8.6795 12.6667 9.12224 12.6667H10.8273C11.3639 12.6667 11.8068 12.6667 12.1676 12.6372C12.5424 12.6066 12.8868 12.5409 13.2104 12.3761C13.7121 12.1204 14.1201 11.7124 14.3758 11.2107C14.5406 10.8871 14.6063 10.5427 14.6369 10.1679C14.6664 9.80713 14.6664 9.36423 14.6664 8.82759V5.17249C14.6664 4.63585 14.6664 4.19295 14.6369 3.83216C14.6063 3.45743 14.5406 3.11296 14.3758 2.7894C14.1201 2.28763 13.7121 1.87969 13.2104 1.62402C12.8868 1.45916 12.5424 1.39345 12.1676 1.36284C11.8068 1.33336 11.3639 1.33337 10.8273 1.33337ZM8.99479 5.00004C8.99479 4.44776 9.44251 4.00004 9.99479 4.00004C10.5471 4.00004 10.9948 4.44776 10.9948 5.00004C10.9948 5.55233 10.5471 6.00004 9.99479 6.00004C9.44251 6.00004 8.99479 5.55233 8.99479 5.00004ZM4.92813 7.80008C5.22175 7.57986 5.63792 7.63849 5.85937 7.93064C5.90047 7.98307 5.94569 8.03241 5.99175 8.08048C6.08995 8.18295 6.23751 8.32196 6.42858 8.46092C6.81329 8.74071 7.34515 9.00008 7.9948 9.00008C8.64444 9.00008 9.17631 8.74071 9.56102 8.46092C9.75209 8.32196 9.89965 8.18295 9.99785 8.08048C10.0439 8.03242 10.0891 7.98306 10.1302 7.93064C10.3517 7.63849 10.7678 7.57986 11.0615 7.80008C11.356 8.02099 11.4157 8.43886 11.1948 8.73341C11.1965 8.73124 11.1925 8.73622 11.1857 8.74479C11.1695 8.76522 11.137 8.8061 11.1259 8.81929C11.0868 8.86587 11.0315 8.92896 10.9605 9.00302C10.8191 9.15055 10.6125 9.34486 10.3452 9.53924C9.81328 9.92612 9.01182 10.3334 7.9948 10.3334C6.97778 10.3334 6.17631 9.92612 5.64435 9.53924C5.37709 9.34486 5.17048 9.15055 5.0291 9.00302C4.95813 8.92896 4.9028 8.86587 4.8637 8.81929C4.84413 8.79597 4.82856 8.77671 4.81707 8.76219C4.58678 8.46467 4.61774 8.03288 4.92813 7.80008ZM5.99479 4.00004C5.44251 4.00004 4.99479 4.44776 4.99479 5.00004C4.99479 5.55233 5.44251 6.00004 5.99479 6.00004C6.54708 6.00004 6.99479 5.55233 6.99479 5.00004C6.99479 4.44776 6.54708 4.00004 5.99479 4.00004Z" fill="#06AED4"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="bar-chart-square-02">
<path id="Solid" fill-rule="evenodd" clip-rule="evenodd" d="M11.8925 1.33331H4.1078C3.75638 1.3333 3.45319 1.33329 3.20348 1.35369C2.93992 1.37523 2.67777 1.42277 2.42552 1.5513C2.04919 1.74305 1.74323 2.04901 1.55148 2.42533C1.42296 2.67759 1.37541 2.93973 1.35388 3.2033C1.33348 3.453 1.33349 3.75617 1.3335 4.10759V11.8923C1.33349 12.2438 1.33348 12.547 1.35388 12.7967C1.37541 13.0602 1.42296 13.3224 1.55148 13.5746C1.74323 13.951 2.04919 14.2569 2.42552 14.4487C2.67777 14.5772 2.93992 14.6247 3.20348 14.6463C3.45319 14.6667 3.75636 14.6667 4.10779 14.6666H11.8925C12.244 14.6667 12.5471 14.6667 12.7969 14.6463C13.0604 14.6247 13.3226 14.5772 13.5748 14.4487C13.9511 14.2569 14.2571 13.951 14.4488 13.5746C14.5774 13.3224 14.6249 13.0602 14.6465 12.7967C14.6669 12.547 14.6668 12.2438 14.6668 11.8924V4.1076C14.6668 3.75618 14.6669 3.45301 14.6465 3.2033C14.6249 2.93973 14.5774 2.67759 14.4488 2.42533C14.2571 2.04901 13.9511 1.74305 13.5748 1.5513C13.3226 1.42277 13.0604 1.37523 12.7969 1.35369C12.5471 1.33329 12.2439 1.3333 11.8925 1.33331ZM11.3335 4.66665C11.3335 4.29846 11.035 3.99998 10.6668 3.99998C10.2986 3.99998 10.0002 4.29846 10.0002 4.66665V11.3333C10.0002 11.7015 10.2986 12 10.6668 12C11.035 12 11.3335 11.7015 11.3335 11.3333V4.66665ZM8.00016 6.66665C8.36835 6.66665 8.66683 6.96512 8.66683 7.33331V11.3333C8.66683 11.7015 8.36835 12 8.00016 12C7.63197 12 7.3335 11.7015 7.3335 11.3333V7.33331C7.3335 6.96512 7.63197 6.66665 8.00016 6.66665ZM5.3335 9.33331C5.70169 9.33331 6.00016 9.63179 6.00016 9.99998V11.3333C6.00016 11.7015 5.70169 12 5.3335 12C4.96531 12 4.66683 11.7015 4.66683 11.3333V9.99998C4.66683 9.63179 4.96531 9.33331 5.3335 9.33331Z" fill="#155EEF"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Some files were not shown because too many files have changed in this diff Show More