| @@ -4,7 +4,6 @@ import React, { useEffect, useMemo } from 'react' | |||
| import { usePathname } from 'next/navigation' | |||
| import useSWR from 'swr' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { useBoolean } from 'ahooks' | |||
| import { | |||
| RiEqualizer2Fill, | |||
| RiEqualizer2Line, | |||
| @@ -44,17 +43,12 @@ type IExtraInfoProps = { | |||
| } | |||
| const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => { | |||
| const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile) | |||
| const { t } = useTranslation() | |||
| const docLink = useDocLink() | |||
| const hasRelatedApps = relatedApps?.data && relatedApps?.data?.length > 0 | |||
| const relatedAppsTotal = relatedApps?.data?.length || 0 | |||
| useEffect(() => { | |||
| setShowTips(!isMobile) | |||
| }, [isMobile, setShowTips]) | |||
| return <div> | |||
| {/* Related apps for desktop */} | |||
| <div className={classNames( | |||
| @@ -55,8 +55,6 @@ const SettingsModal: FC<SettingsModalProps> = ({ | |||
| const { data: embeddingsModelList } = useModelList(ModelTypeEnum.textEmbedding) | |||
| const { | |||
| modelList: rerankModelList, | |||
| defaultModel: rerankDefaultModel, | |||
| currentModel: isRerankDefaultModelValid, | |||
| } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank) | |||
| const { t } = useTranslation() | |||
| const docLink = useDocLink() | |||
| @@ -81,7 +81,6 @@ const CodeBlock: any = memo(({ inline, className, children = '', ...props }: any | |||
| const echartsRef = useRef<any>(null) | |||
| const contentRef = useRef<string>('') | |||
| const processedRef = useRef<boolean>(false) // Track if content was successfully processed | |||
| const instanceIdRef = useRef<string>(`chart-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`) // Unique ID for logging | |||
| const isInitialRenderRef = useRef<boolean>(true) // Track if this is initial render | |||
| const chartInstanceRef = useRef<any>(null) // Direct reference to ECharts instance | |||
| const resizeTimerRef = useRef<NodeJS.Timeout | null>(null) // For debounce handling | |||
| @@ -1,4 +1,4 @@ | |||
| import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' | |||
| import React, { useCallback, useEffect, useRef, useState } from 'react' | |||
| import mermaid, { type MermaidConfig } from 'mermaid' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { ExclamationTriangleIcon } from '@heroicons/react/24/outline' | |||
| @@ -122,14 +122,6 @@ const Flowchart = React.forwardRef((props: { | |||
| const renderTimeoutRef = useRef<NodeJS.Timeout>() | |||
| const [errMsg, setErrMsg] = useState('') | |||
| const [imagePreviewUrl, setImagePreviewUrl] = useState('') | |||
| const [isCodeComplete, setIsCodeComplete] = useState(false) | |||
| const codeCompletionCheckRef = useRef<NodeJS.Timeout>() | |||
| const prevCodeRef = useRef<string>() | |||
| // Create cache key from code, style and theme | |||
| const cacheKey = useMemo(() => { | |||
| return `${props.PrimitiveCode}-${look}-${currentTheme}` | |||
| }, [props.PrimitiveCode, look, currentTheme]) | |||
| /** | |||
| * Renders Mermaid chart | |||
| @@ -537,11 +529,9 @@ const Flowchart = React.forwardRef((props: { | |||
| {isLoading && !svgString && ( | |||
| <div className='px-[26px] py-4'> | |||
| <LoadingAnim type='text'/> | |||
| {!isCodeComplete && ( | |||
| <div className="mt-2 text-sm text-gray-500"> | |||
| {t('common.wait_for_completion', 'Waiting for diagram code to complete...')} | |||
| </div> | |||
| )} | |||
| </div> | |||
| )} | |||
| @@ -1,4 +1,4 @@ | |||
| import React, { useState } from 'react' | |||
| import React from 'react' | |||
| import type { FC } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { RiBookOpenLine } from '@remixicon/react' | |||
| @@ -28,10 +28,8 @@ const Form: FC<FormProps> = React.memo(({ | |||
| }) => { | |||
| const { t, i18n } = useTranslation() | |||
| const docLink = useDocLink() | |||
| const [changeKey, setChangeKey] = useState('') | |||
| const handleFormChange = (key: string, val: string) => { | |||
| setChangeKey(key) | |||
| if (key === 'name') { | |||
| onChange({ ...value, [key]: val }) | |||
| } | |||
| @@ -28,8 +28,8 @@ const RenameDatasetModal = ({ show, dataset, onSuccess, onClose }: RenameDataset | |||
| const [loading, setLoading] = useState(false) | |||
| const [name, setName] = useState<string>(dataset.name) | |||
| const [description, setDescription] = useState<string>(dataset.description) | |||
| const [externalKnowledgeId, setExternalKnowledgeId] = useState<string>(dataset.external_knowledge_info.external_knowledge_id) | |||
| const [externalKnowledgeApiId, setExternalKnowledgeApiId] = useState<string>(dataset.external_knowledge_info.external_knowledge_api_id) | |||
| const externalKnowledgeId = dataset.external_knowledge_info.external_knowledge_id | |||
| const externalKnowledgeApiId = dataset.external_knowledge_info.external_knowledge_api_id | |||
| const onConfirm: MouseEventHandler = async () => { | |||
| if (!name.trim()) { | |||
| @@ -51,7 +51,6 @@ const Apps = ({ | |||
| handleSearch() | |||
| } | |||
| const [currentType, setCurrentType] = useState<string>('') | |||
| const [currCategory, setCurrCategory] = useTabSearchParams({ | |||
| defaultTab: allCategoriesEn, | |||
| disableSearchParams: false, | |||
| @@ -74,28 +73,7 @@ const Apps = ({ | |||
| }, | |||
| ) | |||
| const filteredList = useMemo(() => { | |||
| if (currCategory === allCategoriesEn) { | |||
| if (!currentType) | |||
| return allList | |||
| else if (currentType === 'chatbot') | |||
| return allList.filter(item => (item.app.mode === 'chat' || item.app.mode === 'advanced-chat')) | |||
| else if (currentType === 'agent') | |||
| return allList.filter(item => (item.app.mode === 'agent-chat')) | |||
| else | |||
| return allList.filter(item => (item.app.mode === 'workflow')) | |||
| } | |||
| else { | |||
| if (!currentType) | |||
| return allList.filter(item => item.category === currCategory) | |||
| else if (currentType === 'chatbot') | |||
| return allList.filter(item => (item.app.mode === 'chat' || item.app.mode === 'advanced-chat') && item.category === currCategory) | |||
| else if (currentType === 'agent') | |||
| return allList.filter(item => (item.app.mode === 'agent-chat') && item.category === currCategory) | |||
| else | |||
| return allList.filter(item => (item.app.mode === 'workflow') && item.category === currCategory) | |||
| } | |||
| }, [currentType, currCategory, allCategoriesEn, allList]) | |||
| const filteredList = allList.filter(item => currCategory === allCategoriesEn || item.category === currCategory) | |||
| const searchFilteredList = useMemo(() => { | |||
| if (!searchKeywords || !filteredList || filteredList.length === 0) | |||
| @@ -49,7 +49,6 @@ const SideBar: FC<IExploreSideBarProps> = ({ | |||
| const segments = useSelectedLayoutSegments() | |||
| const lastSegment = segments.slice(-1)[0] | |||
| const isDiscoverySelected = lastSegment === 'apps' | |||
| const isChatSelected = lastSegment === 'chat' | |||
| const { installedApps, setInstalledApps, setIsFetchingInstalledApps } = useContext(ExploreContext) | |||
| const { isFetching: isFetchingInstalledApps, data: ret, refetch: fetchInstalledAppList } = useGetInstalledApps() | |||
| const { mutateAsync: uninstallApp } = useUninstallApp() | |||
| @@ -1,11 +1,10 @@ | |||
| 'use client' | |||
| import React, { useCallback, useEffect, useRef, useState } from 'react' | |||
| import React, { useCallback, useEffect, useState } from 'react' | |||
| import { t } from 'i18next' | |||
| import copy from 'copy-to-clipboard' | |||
| import s from './index.module.css' | |||
| import type { SuccessInvitationResult } from '.' | |||
| import Tooltip from '@/app/components/base/tooltip' | |||
| import { randomString } from '@/utils' | |||
| type IInvitationLinkProps = { | |||
| value: SuccessInvitationResult | |||
| @@ -15,7 +14,6 @@ const InvitationLink = ({ | |||
| value, | |||
| }: IInvitationLinkProps) => { | |||
| const [isCopied, setIsCopied] = useState(false) | |||
| const selector = useRef(`invite-link-${randomString(4)}`) | |||
| const copyHandle = useCallback(() => { | |||
| // No prefix is needed here because the backend has already processed it | |||
| @@ -1,7 +1,6 @@ | |||
| import type { FC } from 'react' | |||
| import { useEffect, useRef, useState } from 'react' | |||
| import type { ModelParameterRule } from '../declarations' | |||
| import { useLanguage } from '../hooks' | |||
| import { isNullOrUndefined } from '../utils' | |||
| import cn from '@/utils/classnames' | |||
| import Switch from '@/app/components/base/switch' | |||
| @@ -10,7 +9,6 @@ import Slider from '@/app/components/base/slider' | |||
| import Radio from '@/app/components/base/radio' | |||
| import { SimpleSelect } from '@/app/components/base/select' | |||
| import TagInput from '@/app/components/base/tag-input' | |||
| import { useTranslation } from 'react-i18next' | |||
| export type ParameterValue = number | string | string[] | boolean | undefined | |||
| @@ -28,8 +26,6 @@ const ParameterItem: FC<ParameterItemProps> = ({ | |||
| onSwitch, | |||
| isInWorkflow, | |||
| }) => { | |||
| const { t } = useTranslation() | |||
| const language = useLanguage() | |||
| const [localValue, setLocalValue] = useState(value) | |||
| const numberInputRef = useRef<HTMLInputElement>(null) | |||
| @@ -30,7 +30,7 @@ const TagsFilter = ({ | |||
| const { t } = useMixedTranslation(locale) | |||
| const [open, setOpen] = useState(false) | |||
| const [searchText, setSearchText] = useState('') | |||
| const { tags: options, tagsMap } = useTags(t) | |||
| const { tags: options } = useTags(t) | |||
| const filteredOptions = options.filter(option => option.label.toLowerCase().includes(searchText.toLowerCase())) | |||
| const handleCheck = (id: string) => { | |||
| if (tags.includes(id)) | |||
| @@ -38,7 +38,6 @@ const TagsFilter = ({ | |||
| else | |||
| onTagsChange([...tags, id]) | |||
| } | |||
| const selectedTagsLength = tags.length | |||
| return ( | |||
| <PortalToFollowElem | |||
| @@ -45,7 +45,7 @@ export const getPluginDetailLinkInMarketplace = (plugin: Plugin) => { | |||
| } | |||
| export const getMarketplacePluginsByCollectionId = async (collectionId: string, query?: CollectionsAndPluginsSearchParams) => { | |||
| let plugins = [] as Plugin[] | |||
| let plugins: Plugin[] | |||
| try { | |||
| const url = `${MARKETPLACE_API_PREFIX}/collections/${collectionId}/plugins` | |||
| @@ -151,10 +151,9 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| const pendingTaskList = allTaskList.filter(task => task.status === TaskStatus.pending) | |||
| const noPendingTask = pendingTaskList.length === 0 | |||
| const showTaskList = allTaskList.filter(task => task.status !== TaskStatus.pending) | |||
| const [currGroupNum, doSetCurrGroupNum] = useState(0) | |||
| const currGroupNumRef = useRef(0) | |||
| const setCurrGroupNum = (num: number) => { | |||
| doSetCurrGroupNum(num) | |||
| currGroupNumRef.current = num | |||
| } | |||
| const getCurrGroupNum = () => { | |||
| @@ -164,10 +163,8 @@ const TextGeneration: FC<IMainProps> = ({ | |||
| const allFailedTaskList = allTaskList.filter(task => task.status === TaskStatus.failed) | |||
| const allTasksFinished = allTaskList.every(task => task.status === TaskStatus.completed) | |||
| const allTasksRun = allTaskList.every(task => [TaskStatus.completed, TaskStatus.failed].includes(task.status)) | |||
| const [batchCompletionRes, doSetBatchCompletionRes] = useState<Record<string, string>>({}) | |||
| const batchCompletionResRef = useRef<Record<string, string>>({}) | |||
| const setBatchCompletionRes = (res: Record<string, string>) => { | |||
| doSetBatchCompletionRes(res) | |||
| batchCompletionResRef.current = res | |||
| } | |||
| const getBatchCompletionRes = () => batchCompletionResRef.current | |||
| @@ -23,7 +23,7 @@ const useStickyScroll = ({ | |||
| return | |||
| const { height: wrapHeight, top: wrapTop } = wrapDom.getBoundingClientRect() | |||
| const { top: nextToStickyTop } = stickyDOM.getBoundingClientRect() | |||
| let scrollPositionNew = ScrollPosition.belowTheWrap | |||
| let scrollPositionNew: ScrollPosition | |||
| if (nextToStickyTop - wrapTop >= wrapHeight) | |||
| scrollPositionNew = ScrollPosition.belowTheWrap | |||
| @@ -444,7 +444,7 @@ export const useFetchToolsData = () => { | |||
| workflowTools: workflowTools || [], | |||
| }) | |||
| } | |||
| if(type === 'mcp') { | |||
| if (type === 'mcp') { | |||
| const mcpTools = await fetchAllMCPTools() | |||
| workflowStore.setState({ | |||
| @@ -500,18 +500,17 @@ export const useToolIcon = (data: Node['data']) => { | |||
| const mcpTools = useStore(s => s.mcpTools) | |||
| const toolIcon = useMemo(() => { | |||
| if(!data) | |||
| if (!data) | |||
| return '' | |||
| if (data.type === BlockEnum.Tool) { | |||
| let targetTools = buildInTools | |||
| let targetTools = workflowTools | |||
| if (data.provider_type === CollectionType.builtIn) | |||
| targetTools = buildInTools | |||
| else if (data.provider_type === CollectionType.custom) | |||
| targetTools = customTools | |||
| else if (data.provider_type === CollectionType.mcp) | |||
| targetTools = mcpTools | |||
| else | |||
| targetTools = workflowTools | |||
| return targetTools.find(toolWithProvider => canFindTool(toolWithProvider.id, data.provider_id))?.icon | |||
| } | |||
| }, [data, buildInTools, customTools, mcpTools, workflowTools]) | |||
| @@ -11,7 +11,6 @@ import { | |||
| import { | |||
| useNodeDataUpdate, | |||
| useNodesInteractions, | |||
| useNodesSyncDraft, | |||
| } from '../../../hooks' | |||
| import { type Node, NodeRunningStatus } from '../../../types' | |||
| import { canRunBySingle } from '../../../utils' | |||
| @@ -30,7 +29,6 @@ const NodeControl: FC<NodeControlProps> = ({ | |||
| const [open, setOpen] = useState(false) | |||
| const { handleNodeDataUpdate } = useNodeDataUpdate() | |||
| const { handleNodeSelect } = useNodesInteractions() | |||
| const { handleSyncWorkflowDraft } = useNodesSyncDraft() | |||
| const isSingleRunning = data._singleRunningStatus === NodeRunningStatus.Running | |||
| const handleOpenChange = useCallback((newOpen: boolean) => { | |||
| setOpen(newOpen) | |||
| @@ -198,7 +198,6 @@ const BasePanel: FC<BasePanelProps> = ({ | |||
| isShowSingleRun, | |||
| hideSingleRun, | |||
| runningStatus, | |||
| handleStop, | |||
| runInputData, | |||
| runInputDataRef, | |||
| runResult, | |||
| @@ -36,6 +36,7 @@ const useConfig = (id: string, payload: ListFilterNodeType) => { | |||
| const { inputs, setInputs } = useNodeCrud<ListFilterNodeType>(id, payload) | |||
| const { getCurrentVariableType } = useWorkflowVariables() | |||
| const getType = useCallback((variable?: ValueSelector) => { | |||
| const varType = getCurrentVariableType({ | |||
| parentNode: isInIteration ? iterationNode : loopNode, | |||
| @@ -44,7 +45,7 @@ const useConfig = (id: string, payload: ListFilterNodeType) => { | |||
| isChatMode, | |||
| isConstant: false, | |||
| }) | |||
| let itemVarType = VarType.string | |||
| let itemVarType = varType | |||
| switch (varType) { | |||
| case VarType.arrayNumber: | |||
| itemVarType = VarType.number | |||
| @@ -58,8 +59,6 @@ const useConfig = (id: string, payload: ListFilterNodeType) => { | |||
| case VarType.arrayObject: | |||
| itemVarType = VarType.object | |||
| break | |||
| default: | |||
| itemVarType = varType | |||
| } | |||
| return { varType, itemVarType } | |||
| }, [availableNodes, getCurrentVariableType, inputs.variable, isChatMode, isInIteration, iterationNode, loopNode]) | |||
| @@ -163,7 +163,7 @@ export default combine( | |||
| 'sonarjs/single-char-in-character-classes': 'off', | |||
| 'sonarjs/anchor-precedence': 'warn', | |||
| 'sonarjs/updated-loop-counter': 'off', | |||
| 'sonarjs/no-dead-store': 'warn', | |||
| 'sonarjs/no-dead-store': 'error', | |||
| 'sonarjs/no-duplicated-branches': 'warn', | |||
| 'sonarjs/max-lines': 'warn', // max 1000 lines | |||
| 'sonarjs/no-variable-usage-before-declaration': 'error', | |||