System plugin (#2091)

* System template (#2082)

* feat: system plugin (#2024)

* add plugin cost & change plugin avatar (#2030)

* add plugin cost & change plugin avatar

* add author

* feat: duckduckgo plugin

* duckduck search

* perf: templates select system plugin

* perf: system plugin avatar

* feat: duckduck plugins

* doc

* perf: plugin classify

* perf: icon avatar component

* perf: system template avatar

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>

* feat: system plugin search

* perf: plugin packages important

* perf: source avatar

* nextconfig

* perf: i18n

* perf: default model

* perf: system plugin author

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
Archer
2024-07-19 14:15:01 +08:00
committed by GitHub
parent 1eedb9caba
commit cf7145ab54
165 changed files with 2643 additions and 557 deletions

View File

@@ -23,7 +23,7 @@ import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { compressImgFileAndUpload } from '@/web/common/file/controller';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useRouter } from 'next/router';

View File

@@ -27,7 +27,7 @@ import { useTranslation } from 'next-i18next';
import { useQuery } from '@tanstack/react-query';
import { useUserStore } from '@/web/support/user/useUserStore';
import { getTeamMembers } from '@/web/support/user/team/api';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MySelect from '@fastgpt/web/components/common/MySelect';
import { formatNumber } from '@fastgpt/global/common/math/tools';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';

View File

@@ -1,26 +1,55 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import type { NextApiResponse } from 'next';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node.d';
import { NextAPI } from '@/service/middleware/entry';
import { getCommunityPluginsTemplateList } from '@fastgpt/plugins/register';
import { getSystemPluginTemplates } from '@fastgpt/plugins/register';
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { ApiRequestProps } from '@fastgpt/service/type/next';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
export type GetSystemPluginTemplatesBody = {
searchKey?: string;
parentId: ParentIdType;
};
async function handler(
req: NextApiRequest,
req: ApiRequestProps<GetSystemPluginTemplatesBody>,
res: NextApiResponse<any>
): Promise<NodeTemplateListItemType[]> {
await authCert({ req, authToken: true });
// const data: NodeTemplateListItemType[] =
// global.communityPlugins?.map((plugin) => ({
// id: plugin.id,
// templateType: plugin.templateType ?? FlowNodeTemplateTypeEnum.other,
// flowNodeType: FlowNodeTypeEnum.pluginModule,
// avatar: plugin.avatar,
// name: plugin.name,
// intro: plugin.intro
// })) || [];
const { searchKey, parentId } = req.body;
return getCommunityPluginsTemplateList();
const formatParentId = parentId || null;
return getSystemPluginTemplates().then((res) =>
res
// Just show the active plugins
.filter((item) => item.isActive)
.map<NodeTemplateListItemType>((plugin) => ({
id: plugin.id,
isFolder: plugin.isFolder,
parentId: plugin.parentId,
templateType: plugin.templateType ?? FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.pluginModule,
avatar: plugin.avatar,
name: plugin.name,
intro: plugin.intro,
isTool: plugin.isTool,
currentCost: plugin.currentCost,
author: plugin.author
}))
.filter((item) => {
if (searchKey) {
if (item.isFolder) return false;
const regx = new RegExp(`${replaceRegChars(searchKey)}`, 'i');
return regx.test(item.name) || regx.test(item.intro || '');
}
return item.parentId === formatParentId;
})
);
}
export default NextAPI(handler);

View File

@@ -0,0 +1,35 @@
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { ParentIdType, ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
import { getSystemPluginTemplates } from '@fastgpt/plugins/register';
export type pathQuery = {
parentId: ParentIdType;
};
export type pathBody = {};
export type pathResponse = Promise<ParentTreePathItemType[]>;
async function handler(
req: ApiRequestProps<pathBody, pathQuery>,
res: ApiResponseType<any>
): Promise<pathResponse> {
const { parentId } = req.query;
if (!parentId) return [];
const plugins = await getSystemPluginTemplates();
const plugin = plugins.find((item) => item.id === parentId);
if (!plugin) return [];
return [
{
parentId: plugin.id,
parentName: plugin.name
}
];
}
export default NextAPI(handler);

View File

@@ -30,7 +30,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
const response = await ai.chat.completions.create({
model: 'gpt-3.5-turbo',
model: 'gpt-4o-mini',
max_tokens: 1,
messages: [{ role: 'user', content: 'hi' }]
});

View File

@@ -16,7 +16,7 @@ import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { compressImgFileAndUpload } from '@/web/common/file/controller';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';

View File

@@ -13,7 +13,7 @@ import {
import { useRouter } from 'next/router';
import { AppSchema } from '@fastgpt/global/core/app/type.d';
import { useTranslation } from 'next-i18next';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import TagsEditModal from '../TagsEditModal';
import { useSystemStore } from '@/web/common/system/useSystemStore';

View File

@@ -18,7 +18,7 @@ import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import dynamic from 'next/dynamic';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import VariableEdit from '@/components/core/app/VariableEdit';
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
@@ -242,8 +242,15 @@ const EditForm = ({
})
}
>
<Avatar src={item.avatar} w={'18px'} mr={1} />
<Box flex={'1 0 0'} w={0} className={'textEllipsis'} fontSize={'sm'}>
<Avatar src={item.avatar} w={'1.5rem'} borderRadius={'sm'} />
<Box
ml={2}
flex={'1 0 0'}
w={0}
className={'textEllipsis'}
fontSize={'sm'}
color={'myGray.900'}
>
{item.name}
</Box>
</Flex>
@@ -292,8 +299,15 @@ const EditForm = ({
borderColor: 'primary.300'
}}
>
<Avatar src={item.avatar} w={'1rem'} mr={1} />
<Box flex={'1 0 0'} w={0} className={'textEllipsis'} fontSize={'sm'}>
<Avatar src={item.avatar} w={'1.5rem'} borderRadius={'sm'} />
<Box
ml={2}
flex={'1 0 0'}
w={0}
className={'textEllipsis'}
fontSize={'sm'}
color={'myGray.900'}
>
{item.name}
</Box>
<DeleteIcon

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
@@ -26,21 +26,27 @@ import {
FlowNodeTemplateType,
NodeTemplateListItemType
} from '@fastgpt/global/core/workflow/type/node.d';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { AddIcon } from '@chakra-ui/icons';
import { getPreviewPluginNode, getSystemPlugTemplates } from '@/web/core/app/api/plugin';
import {
getPreviewPluginNode,
getSystemPlugTemplates,
getSystemPluginPaths
} from '@/web/core/app/api/plugin';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { useForm } from 'react-hook-form';
import JsonEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import { getTeamPlugTemplates } from '@/web/core/app/api/plugin';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { getAppFolderPath } from '@/web/core/app/api/app';
import FolderPath from '@/components/common/folder/Path';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import CoseTooltip from '@/components/core/app/plugin/CoseTooltip';
import { useSystemStore } from '@/web/common/system/useSystemStore';
type Props = {
selectedTools: FlowNodeTemplateType[];
@@ -63,9 +69,7 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
const { data: templates = [], loading: isLoading } = useRequest2(
async () => {
if (templateType === TemplateTypeEnum.systemPlugin) {
return (await getSystemPlugTemplates()).filter(
(item) => item.isTool && item.name.toLowerCase().includes(searchKey.toLowerCase())
);
return getSystemPlugTemplates({ parentId, searchKey });
} else if (templateType === TemplateTypeEnum.teamPlugin) {
return getTeamPlugTemplates({
parentId,
@@ -82,10 +86,20 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
}
);
const { data: paths = [] } = useRequest2(() => getAppFolderPath(parentId), {
manual: false,
refreshDeps: [parentId]
});
const { data: paths = [] } = useRequest2(
() => {
if (templateType === TemplateTypeEnum.teamPlugin) return getAppFolderPath(parentId);
return getSystemPluginPaths(parentId);
},
{
manual: false,
refreshDeps: [parentId]
}
);
useEffect(() => {
setParentId('');
}, [templateType, searchKey]);
return (
<MyModal
@@ -129,7 +143,7 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
</InputGroup>
</Box>
{/* route components */}
{templateType === TemplateTypeEnum.teamPlugin && !searchKey && parentId && (
{!searchKey && parentId && (
<Flex mt={2} px={[3, 6]}>
<FolderPath
paths={paths}
@@ -145,6 +159,7 @@ const ToolSelectModal = ({ onClose, ...props }: Props & { onClose: () => void })
templates={templates}
isLoadingData={isLoading}
setParentId={setParentId}
showCost={templateType === TemplateTypeEnum.systemPlugin}
{...props}
/>
</MyBox>
@@ -160,13 +175,16 @@ const RenderList = React.memo(function RenderList({
isLoadingData,
onAddTool,
onRemoveTool,
setParentId
setParentId,
showCost
}: Props & {
templates: NodeTemplateListItemType[];
isLoadingData: boolean;
setParentId: React.Dispatch<React.SetStateAction<ParentIdType>>;
showCost?: boolean;
}) {
const { t } = useTranslation();
const { feConfigs } = useSystemStore();
const [configTool, setConfigTool] = useState<FlowNodeTemplateType>();
const onCloseConfigTool = useCallback(() => setConfigTool(undefined), []);
@@ -195,57 +213,78 @@ const RenderList = React.memo(function RenderList({
const selected = selectedTools.some((tool) => tool.pluginId === item.id);
return (
<Flex
<MyTooltip
key={item.id}
alignItems={'center'}
p={[4, 5]}
_notLast={{
borderBottomWidth: '1px',
borderBottomColor: 'myGray.150'
}}
_hover={{
bg: 'myGray.50'
}}
placement={'bottom'}
shouldWrapChildren={false}
label={
<Box>
<Flex alignItems={'center'}>
<Avatar
src={item.avatar}
w={'1.75rem'}
objectFit={'contain'}
borderRadius={'sm'}
/>
<Box fontWeight={'bold'} ml={2} color={'myGray.900'}>
{t(item.name as any)}
</Box>
</Flex>
<Box mt={2} color={'myGray.500'}>
{t(item.intro as any) || t('common:core.workflow.Not intro')}
</Box>
{showCost && <CoseTooltip cost={item.currentCost} />}
</Box>
}
>
<Avatar
src={item.avatar}
w={['26px', '32px']}
objectFit={'contain'}
borderRadius={'0'}
/>
<Box ml={5} flex={'1 0 0'}>
<Box color={'black'}>{t(item.name as any)}</Box>
{item.intro && (
<Box className="textEllipsis3" color={'myGray.500'} fontSize={'xs'}>
{t(item.intro as any)}
<Flex
alignItems={'center'}
position={'relative'}
p={[4, 5]}
_notLast={{
borderBottomWidth: '1px',
borderBottomColor: 'myGray.150'
}}
_hover={{
bg: 'myGray.50'
}}
>
<Avatar src={item.avatar} w={'2rem'} objectFit={'contain'} borderRadius={'md'} />
<Box ml={3} flex={'1 0 0'} color={'myGray.900'}>
{t(item.name as any)}
</Box>
{item.author !== undefined && (
<Box fontSize={'xs'} mr={3}>
{`by ${item.author || feConfigs.systemTitle}`}
</Box>
)}
</Box>
{selected ? (
<Button
size={'sm'}
variant={'grayDanger'}
leftIcon={<MyIcon name={'delete'} w={'14px'} />}
onClick={() => onRemoveTool(item)}
>
{t('common:common.Remove')}
</Button>
) : item.isFolder ? (
<Button size={'sm'} variant={'whiteBase'} onClick={() => setParentId(item.id)}>
{t('common:common.Open')}
</Button>
) : (
<Button
size={'sm'}
variant={'whiteBase'}
leftIcon={<AddIcon fontSize={'10px'} />}
isLoading={isLoading}
onClick={() => onClickAdd(item)}
>
{t('common:common.Add')}
</Button>
)}
</Flex>
{selected ? (
<Button
size={'sm'}
variant={'grayDanger'}
leftIcon={<MyIcon name={'delete'} w={'14px'} />}
onClick={() => onRemoveTool(item)}
>
{t('common:common.Remove')}
</Button>
) : item.isFolder ? (
<Button size={'sm'} variant={'whiteBase'} onClick={() => setParentId(item.id)}>
{t('common:common.Open')}
</Button>
) : (
<Button
size={'sm'}
variant={'whiteBase'}
leftIcon={<AddIcon fontSize={'10px'} />}
isLoading={isLoading}
onClick={() => onClickAdd(item)}
>
{t('common:common.Add')}
</Button>
)}
</Flex>
</MyTooltip>
);
})}
{!!configTool && (

View File

@@ -10,7 +10,6 @@ import { WorkflowContext, getWorkflowStore } from '../WorkflowComponents/context
import { useInterval } from 'ahooks';
import { AppContext, TabEnum } from '../context';
import RouteTab from '../RouteTab';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
import { useRouter } from 'next/router';

View File

@@ -4,7 +4,7 @@ import { useContextSelector } from 'use-context-selector';
import { AppContext, TabEnum } from '../context';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'next-i18next';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyMenu from '@fastgpt/web/components/common/MyMenu';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useI18n } from '@/web/context/I18n';
@@ -159,7 +159,7 @@ const AppCard = ({ showSaveStatus }: { showSaveStatus: boolean }) => {
return (
<HStack>
<InfoMenu>
<Avatar src={appDetail.avatar} w={'1.75rem'} />
<Avatar src={appDetail.avatar} w={'1.75rem'} borderRadius={'md'} />
</InfoMenu>
<Box>
<InfoMenu>

View File

@@ -1,17 +1,30 @@
import React, { useCallback, useMemo, useState } from 'react';
import { Box, Flex, IconButton, Input, InputGroup, InputLeftElement, css } from '@chakra-ui/react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
Box,
Divider,
Flex,
IconButton,
Input,
InputGroup,
InputLeftElement,
css
} from '@chakra-ui/react';
import type {
NodeTemplateListItemType,
NodeTemplateListType
} from '@fastgpt/global/core/workflow/type/node.d';
import { useViewport, XYPosition } from 'reactflow';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { nodeTemplate2FlowNode } from '@/web/core/workflow/utils';
import { useTranslation } from 'next-i18next';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { getPreviewPluginNode, getSystemPlugTemplates } from '@/web/core/app/api/plugin';
import {
getPreviewPluginNode,
getSystemPlugTemplates,
getSystemPluginPaths
} from '@/web/core/app/api/plugin';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { workflowNodeTemplateList } from '@fastgpt/web/core/workflow/constants';
@@ -33,6 +46,7 @@ import { useWorkflowUtils } from './hooks/useUtils';
import { moduleTemplatesFlat } from '@fastgpt/global/core/workflow/template/constants';
import { cloneDeep } from 'lodash';
import { useSystem } from '@fastgpt/web/hooks/useSystem';
import CoseTooltip from '@/components/core/app/plugin/CoseTooltip';
type ModuleTemplateListProps = {
isOpen: boolean;
@@ -40,6 +54,7 @@ type ModuleTemplateListProps = {
};
type RenderListProps = {
templates: NodeTemplateListItemType[];
type: TemplateTypeEnum;
onClose: () => void;
parentId: ParentIdType;
setParentId: React.Dispatch<React.SetStateAction<ParentIdType>>;
@@ -51,7 +66,7 @@ enum TemplateTypeEnum {
'teamPlugin' = 'teamPlugin'
}
const sliderWidth = 390;
const sliderWidth = 420;
const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
const { t } = useTranslation();
@@ -104,7 +119,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
refreshDeps: [basicNodeTemplates, nodeList, hasToolNode, templateType, searchKey, parentId]
}
);
const { data: teamApps, loading: isLoadingTeamApp } = useRequest2(
const { data: teamAndSystemApps, loading: isLoadingTeamApp } = useRequest2(
async () => {
if (templateType === TemplateTypeEnum.teamPlugin) {
return getTeamPlugTemplates({
@@ -113,6 +128,12 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
type: [AppTypeEnum.folder, AppTypeEnum.httpPlugin, AppTypeEnum.plugin]
}).then((res) => res.filter((app) => app.id !== appId));
}
if (templateType === TemplateTypeEnum.systemPlugin) {
return getSystemPlugTemplates({
searchKey,
parentId
});
}
},
{
manual: false,
@@ -120,29 +141,28 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
refreshDeps: [templateType, searchKey, parentId]
}
);
const { data: systemPlugins, loading: isLoadingSystemPlugins } = useRequest2(
async () => {
if (templateType === TemplateTypeEnum.systemPlugin) {
return getSystemPlugTemplates();
}
const isLoading = isLoadingTeamApp;
const templates = useMemo(
() => basicNodes || teamAndSystemApps || [],
[basicNodes, teamAndSystemApps]
);
useEffect(() => {
setParentId('');
}, [templateType, searchKey]);
const { data: paths = [] } = useRequest2(
() => {
if (templateType === TemplateTypeEnum.teamPlugin) return getAppFolderPath(parentId);
return getSystemPluginPaths(parentId);
},
{
manual: false,
refreshDeps: [templateType]
refreshDeps: [parentId]
}
);
const isLoading = isLoadingTeamApp || isLoadingSystemPlugins;
const templates = useMemo(
() => basicNodes || teamApps || systemPlugins || [],
[basicNodes, systemPlugins, teamApps]
);
const { data: paths = [] } = useRequest2(() => getAppFolderPath(parentId), {
manual: false,
refreshDeps: [parentId]
});
const Render = useMemo(() => {
return (
<>
@@ -154,6 +174,7 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
left={0}
bottom={0}
w={`${sliderWidth}px`}
maxW={'100%'}
onClick={onClose}
fontSize={'sm'}
/>
@@ -176,30 +197,35 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
userSelect={'none'}
overflow={isOpen ? 'none' : 'hidden'}
>
{/* Header */}
<Box pl={'20px'} mb={3} pr={'10px'} whiteSpace={'nowrap'} overflow={'hidden'}>
{/* Tabs */}
<Flex flex={'1 0 0'} alignItems={'center'} gap={3}>
<FillRowTabs
list={[
{
icon: 'core/modules/basicNode',
label: t('common:core.module.template.Basic Node'),
value: TemplateTypeEnum.basic
},
{
icon: 'core/modules/systemPlugin',
label: t('common:core.module.template.System Plugin'),
value: TemplateTypeEnum.systemPlugin
},
{
icon: 'core/modules/teamPlugin',
label: t('common:core.module.template.Team Plugin'),
value: TemplateTypeEnum.teamPlugin
}
]}
py={'5px'}
value={templateType}
onChange={(e) => setTemplateType(e as TemplateTypeEnum)}
/>
<Box flex={'1 0 0'}>
<FillRowTabs
list={[
{
icon: 'core/modules/basicNode',
label: t('core.module.template.Basic Node'),
value: TemplateTypeEnum.basic
},
{
icon: 'core/modules/systemPlugin',
label: t('core.module.template.System Plugin'),
value: TemplateTypeEnum.systemPlugin
},
{
icon: 'core/modules/teamPlugin',
label: t('core.module.template.Team Plugin'),
value: TemplateTypeEnum.teamPlugin
}
]}
width={'100%'}
py={'5px'}
value={templateType}
onChange={(e) => setTemplateType(e as TemplateTypeEnum)}
/>
</Box>
{/* close icon */}
<IconButton
size={'sm'}
@@ -210,7 +236,9 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
onClick={onClose}
/>
</Flex>
{templateType === TemplateTypeEnum.teamPlugin && (
{/* Search */}
{(templateType === TemplateTypeEnum.teamPlugin ||
templateType === TemplateTypeEnum.systemPlugin) && (
<Flex mt={2} alignItems={'center'} h={10}>
<InputGroup mr={4} h={'full'}>
<InputLeftElement h={'full'} alignItems={'center'} display={'flex'}>
@@ -224,28 +252,52 @@ const NodeTemplatesModal = ({ isOpen, onClose }: ModuleTemplateListProps) => {
/>
</InputGroup>
<Box flex={1} />
<Flex
alignItems={'center'}
cursor={'pointer'}
_hover={{
color: 'primary.600'
}}
fontSize={'sm'}
onClick={() => router.push('/app/list')}
>
<Box></Box>
<MyIcon name={'common/rightArrowLight'} w={'14px'} />
{templateType === TemplateTypeEnum.teamPlugin && (
<Flex
alignItems={'center'}
cursor={'pointer'}
_hover={{
color: 'primary.600'
}}
fontSize={'sm'}
onClick={() => router.push('/app/list')}
gap={1}
>
<Box></Box>
<MyIcon name={'common/rightArrowLight'} w={'0.8rem'} />
</Flex>
)}
{templateType === TemplateTypeEnum.systemPlugin &&
feConfigs.systemPluginCourseUrl && (
<Flex
alignItems={'center'}
cursor={'pointer'}
_hover={{
color: 'primary.600'
}}
fontSize={'sm'}
onClick={() => window.open(feConfigs.systemPluginCourseUrl)}
gap={1}
>
<Box></Box>
<MyIcon name={'common/rightArrowLight'} w={'0.8rem'} />
</Flex>
)}
</Flex>
)}
{/* paths */}
{(templateType === TemplateTypeEnum.teamPlugin ||
templateType === TemplateTypeEnum.systemPlugin) &&
!searchKey &&
parentId && (
<Flex alignItems={'center'} mt={2}>
<FolderPath paths={paths} FirstPathDom={null} onClick={setParentId} />
</Flex>
</Flex>
)}
{templateType === TemplateTypeEnum.teamPlugin && !searchKey && parentId && (
<Flex alignItems={'center'} mt={2}>
<FolderPath paths={paths} FirstPathDom={null} onClick={setParentId} />
</Flex>
)}
)}
</Box>
<RenderList
templates={templates}
type={templateType}
onClose={onClose}
parentId={parentId}
setParentId={setParentId}
@@ -262,14 +314,18 @@ export default React.memo(NodeTemplatesModal);
const RenderList = React.memo(function RenderList({
templates,
type,
onClose,
parentId,
setParentId
}: RenderListProps) {
const { t } = useTranslation();
const { appT } = useI18n();
const { feConfigs } = useSystemStore();
const { isPc } = useSystem();
const isSystemPlugin = type === TemplateTypeEnum.systemPlugin;
const { x, y, zoom } = useViewport();
const { setLoading } = useSystemStore();
const { toast } = useToast();
@@ -337,7 +393,7 @@ const RenderList = React.memo(function RenderList({
flowNodeType: templateNode.flowNodeType,
pluginId: templateNode.pluginId
}),
intro: t(templateNode.intro || ('' as any))
intro: t(templateNode.intro as any)
},
position: { x: mouseX, y: mouseY - 20 },
selected: true
@@ -371,9 +427,9 @@ const RenderList = React.memo(function RenderList({
}
})}
>
{item.label && (
{item.label && formatTemplates.length > 1 && (
<Flex>
<Box fontSize={'sm'} fontWeight={'bold'} flex={1}>
<Box fontSize={'sm'} fontWeight={'500'} flex={1} color={'myGray.900'}>
{t(item.label as any)}
</Box>
</Flex>
@@ -385,27 +441,29 @@ const RenderList = React.memo(function RenderList({
key={template.id}
placement={'right'}
label={
<Box>
<Box py={2}>
<Flex alignItems={'center'}>
<Avatar
src={template.avatar}
w={'24px'}
w={'1.75rem'}
objectFit={'contain'}
borderRadius={'0'}
borderRadius={'sm'}
/>
<Box fontWeight={'bold'} ml={3}>
<Box fontWeight={'bold'} ml={3} color={'myGray.900'}>
{t(template.name as any)}
</Box>
</Flex>
<Box mt={2} color={'myGray.500'}>
{t(template.intro as any) || t('common:core.workflow.Not intro')}
</Box>
{isSystemPlugin && <CoseTooltip cost={template.currentCost} />}
</Box>
}
>
<Flex
alignItems={'center'}
p={5}
py={4}
px={3}
cursor={'pointer'}
_hover={{ bg: 'myWhite.600' }}
borderRadius={'sm'}
@@ -436,13 +494,24 @@ const RenderList = React.memo(function RenderList({
>
<Avatar
src={template.avatar}
w={'1.7rem'}
w={'2rem'}
objectFit={'contain'}
borderRadius={'0'}
borderRadius={'md'}
/>
<Box color={'black'} fontSize={'sm'} ml={5} flex={'1 0 0'}>
<Box
color={'myGray.900'}
fontWeight={'500'}
fontSize={'sm'}
ml={3}
flex={'1 0 0'}
>
{t(template.name as any)}
</Box>
{template.author !== undefined && (
<Box fontSize={'xs'} color={'myGray.500'}>
{`by ${template.author || feConfigs.systemTitle}`}
</Box>
)}
</Flex>
</MyTooltip>
))}
@@ -452,7 +521,17 @@ const RenderList = React.memo(function RenderList({
</Box>
</Box>
);
}, [appT, formatTemplates, isPc, onAddNode, onClose, setParentId, t, templates.length]);
}, [
appT,
formatTemplates,
isPc,
isSystemPlugin,
onAddNode,
onClose,
setParentId,
t,
templates.length
]);
return Render;
});

View File

@@ -1,7 +1,7 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Box, Button, Card, Flex } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import type { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
import { useTranslation } from 'next-i18next';
import { useEditTitle } from '@/web/common/hooks/useEditTitle';
@@ -137,7 +137,7 @@ const NodeCard = (props: Props) => {
{/* avatar and name */}
<Flex alignItems={'center'}>
<Avatar src={avatar} borderRadius={'0'} objectFit={'contain'} w={'30px'} h={'30px'} />
<Avatar src={avatar} borderRadius={'sm'} objectFit={'contain'} w={'30px'} h={'30px'} />
<Box ml={3} fontSize={'md'} fontWeight={'medium'}>
{t(name as any)}
</Box>

View File

@@ -19,7 +19,7 @@ import { AppContext } from '@/pages/app/detail/components/context';
const MultipleRowSelect = dynamic(
() => import('@fastgpt/web/components/common/MySelect/MultipleRowSelect')
);
const Avatar = dynamic(() => import('@/components/Avatar'));
const Avatar = dynamic(() => import('@fastgpt/web/components/common/Avatar'));
type SelectProps = {
value?: ReferenceValueProps;
@@ -120,8 +120,8 @@ export const useReference = ({
return {
label: (
<Flex alignItems={'center'}>
<Avatar mr={1} src={node.avatar} w={'14px'} borderRadius={'ms'} />
<Box>{t(node.name as any)}</Box>
<Avatar src={node.avatar} w={'1.25rem'} borderRadius={'xs'} />
<Box ml={1}>{t(node.name as any)}</Box>
</Flex>
),
value: node.nodeId,

View File

@@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
import type { RenderInputProps } from '../type';
import { Box, Button, useDisclosure } from '@chakra-ui/react';
import { SelectAppItemType } from '@fastgpt/global/core/workflow/template/system/runApp/type';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import SelectAppModal from '../../../../SelectAppModal';
import { useTranslation } from 'next-i18next';
import { useContextSelector } from 'use-context-selector';

View File

@@ -3,7 +3,7 @@ import type { RenderInputProps } from '../type';
import { Box, Button, Flex, Grid, useDisclosure, useTheme } from '@chakra-ui/react';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { SelectedDatasetType } from '@fastgpt/global/core/workflow/api';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { useQuery } from '@tanstack/react-query';
import { useTranslation } from 'next-i18next';
import { DatasetSearchModeEnum } from '@fastgpt/global/core/dataset/constants';

View File

@@ -18,9 +18,8 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import { postCreateApp } from '@/web/core/app/api';
import { useRouter } from 'next/router';
import { simpleBotTemplates, workflowTemplates, pluginTemplates } from '@/web/core/app/templates';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { useTranslation } from 'next-i18next';
@@ -150,7 +149,7 @@ const CreateModal = ({ onClose, type }: { type: CreateAppType; onClose: () => vo
w={['28px', '32px']}
h={['28px', '32px']}
cursor={'pointer'}
borderRadius={'sm'}
borderRadius={'md'}
onClick={onOpenSelectFile}
/>
</MyTooltip>
@@ -195,7 +194,7 @@ const CreateModal = ({ onClose, type }: { type: CreateAppType; onClose: () => vo
}}
>
<Flex alignItems={'center'}>
<Avatar src={item.avatar} borderRadius={'md'} w={'20px'} />
<Avatar src={item.avatar} borderRadius={'md'} w={'1.5rem'} />
<Box ml={3} color={'myGray.900'}>
{t(item.name as any)}
</Box>

View File

@@ -21,7 +21,7 @@ import { compressImgFileAndUpload } from '@/web/common/file/controller';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'next-i18next';
import { HttpPluginImgUrl, MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';

View File

@@ -4,7 +4,7 @@ import { useRouter } from 'next/router';
import { delAppById, putAppById, resumeInheritPer } from '@/web/core/app/api';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import PermissionIconText from '@/components/support/permission/IconText';
import { useI18n } from '@/web/context/I18n';
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { Flex, useTheme, Box } from '@chakra-ui/react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import ToolMenu from './ToolMenu';
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
import { useTranslation } from 'next-i18next';

View File

@@ -3,7 +3,7 @@ import { Box, Button, Flex, useTheme, IconButton } from '@chakra-ui/react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useEditTitle } from '@/web/common/hooks/useEditTitle';
import { useRouter } from 'next/router';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
@@ -145,7 +145,7 @@ const ChatHistorySlider = ({
})
}
>
<Avatar src={appAvatar} />
<Avatar src={appAvatar} borderRadius={'md'} />
<Box flex={'1 0 0'} w={0} ml={2} fontWeight={'bold'} className={'textEllipsis'}>
{appName}
</Box>

View File

@@ -4,7 +4,7 @@ import { useMarkdown } from '@/web/common/hooks/useMarkdown';
import dynamic from 'next/dynamic';
const Markdown = dynamic(() => import('@/components/Markdown'));
const Avatar = dynamic(() => import('@/components/Avatar'));
const Avatar = dynamic(() => import('@fastgpt/web/components/common/Avatar'));
const Empty = ({
showChatProblem,

View File

@@ -3,7 +3,7 @@ import { Flex, Box, IconButton, HStack } from '@chakra-ui/react';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { AppListItemType } from '@fastgpt/global/core/app/type';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
import MyPopover from '@fastgpt/web/components/common/MyPopover/index';
@@ -152,7 +152,7 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
onClick: () => onChangeApp(item._id)
})}
>
<Avatar src={item.avatar} w={'24px'} />
<Avatar src={item.avatar} w={'1.5rem'} borderRadius={'md'} />
<Box ml={2} className={'textEllipsis'}>
{item.name}
</Box>

View File

@@ -8,7 +8,7 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useForm } from 'react-hook-form';
import { compressImgFileAndUpload } from '@/web/common/file/controller';
import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useTranslation } from 'next-i18next';
import { useSystemStore } from '@/web/common/system/useSystemStore';

View File

@@ -2,7 +2,7 @@ import React, { useCallback } from 'react';
import { useTranslation } from 'next-i18next';
import { Box, Flex, IconButton, useTheme, Progress } from '@chakra-ui/react';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
import DatasetTypeTag from '@/components/core/dataset/DatasetTypeTag';
import MyIcon from '@fastgpt/web/components/common/Icon';

View File

@@ -8,7 +8,7 @@ import { useToast } from '@fastgpt/web/hooks/useToast';
import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { postCreateDataset } from '@/web/core/dataset/api';

View File

@@ -8,7 +8,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { useRouter } from 'next/router';
import PermissionIconText from '@/components/support/permission/IconText';
import DatasetTypeTag from '@/components/core/dataset/DatasetTypeTag';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { DatasetItemType } from '@fastgpt/global/core/dataset/type';

View File

@@ -10,7 +10,7 @@ import {
useTheme,
Grid
} from '@chakra-ui/react';
import Avatar from '@/components/Avatar';
import Avatar from '@fastgpt/web/components/common/Avatar';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import MyModal from '@fastgpt/web/components/common/MyModal';
import MyIcon from '@fastgpt/web/components/common/Icon';