From 5bc5cb3db212741a23dfd5ca5963eb4e84712545 Mon Sep 17 00:00:00 2001 From: xilesun <2013xile@gmail.com> Date: Tue, 22 Jul 2025 09:30:20 +0800 Subject: [PATCH] feat: form filler 2.0 --- .../components/subModel/AddSubModelButton.tsx | 2 +- .../ai-employees/chatbox/ChatButton.tsx | 58 ++++---- .../chatbox/hooks/useChatBoxActions.ts | 11 +- .../src/client/ai-employees/chatbox/utils.ts | 35 +++-- .../ai-employees/context/data-modeling.tsx | 131 ------------------ .../ai-employees/context/flow-models.tsx | 71 ++++++++++ .../models/AIEmployeeShortcutListModel.tsx | 29 ++-- .../flow/models/AIEmployeeShortcutModel.tsx | 69 +++++++-- .../ai-employees/form-filler/tools/index.ts | 28 ++++ .../src/client/ai-employees/icon.png | Bin 19471 -> 16422 bytes .../selector/AISelectorProvider.tsx | 6 +- .../ai-employees/shortcuts/ShortcutList.tsx | 106 ++++++++++++++ .../ai-employees/shortcuts/useShortcuts.ts | 66 +++++++++ .../src/client/ai-employees/types.ts | 8 +- .../@nocobase/plugin-ai/src/client/index.tsx | 19 +-- .../Configuration/schemas/collections.ts | 76 ---------- 16 files changed, 410 insertions(+), 305 deletions(-) delete mode 100644 packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/data-modeling.tsx create mode 100644 packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/flow-models.tsx create mode 100644 packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/form-filler/tools/index.ts create mode 100644 packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx create mode 100644 packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/useShortcuts.ts diff --git a/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx b/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx index 87221bb14ce..9d0de01765e 100644 --- a/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx +++ b/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx @@ -23,7 +23,7 @@ import { buildSubModelGroups, buildSubModelItems } from './utils'; export interface SubModelItem { key?: string; - label?: string; + label?: string | React.ReactNode; type?: 'group' | 'divider'; disabled?: boolean; icon?: React.ReactNode; diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx index b27376a9286..ed6f0ca4d7b 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx @@ -7,18 +7,18 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import React, { useMemo } from 'react'; -import { FloatButton, Avatar, Dropdown } from 'antd'; +import React, { useMemo, useState } from 'react'; +import { FloatButton, Avatar, Dropdown, Button, Divider } from 'antd'; import icon from '../icon.png'; import { css } from '@emotion/css'; import { useAIEmployeesContext } from '../AIEmployeesProvider'; import { AIEmployeeListItem } from '../AIEmployeeListItem'; import { useAISelectionContext } from '../selector/AISelectorProvider'; -import { PauseCircleFilled } from '@ant-design/icons'; +import { PauseCircleFilled, RightOutlined, LeftOutlined } from '@ant-design/icons'; import { useToken } from '@nocobase/client'; -import { FlowModelRenderer, useFlowEngine } from '@nocobase/flow-engine'; import { useChatBoxStore } from './stores/chat-box'; import { useChatBoxActions } from './hooks/useChatBoxActions'; +import { ShortcutList } from '../shortcuts/ShortcutList'; export const ChatButton: React.FC = () => { const { aiEmployees } = useAIEmployeesContext(); @@ -30,13 +30,6 @@ export const ChatButton: React.FC = () => { const { stopSelect, selectable } = useAISelectionContext(); const { token } = useToken(); - // const flowEngine = useFlowEngine(); - // - // const model = useMemo(() => { - // return flowEngine.createModel({ - // use: 'AIEmployeeShortcutListModel', - // }); - // }, [flowEngine]); const items = useMemo(() => { return aiEmployees?.map((employee) => ({ @@ -58,36 +51,39 @@ export const ChatButton: React.FC = () => { return (
- {/* */} + {!selectable ? ( - } + { setOpen(!open); }} /> ) : ( - { + const app = useApp(); const t = useT(); + const open = useChatBoxStore.use.open(); const setOpen = useChatBoxStore.use.setOpen(); const setSenderValue = useChatBoxStore.use.setSenderValue(); const setTaskVariables = useChatBoxStore.use.setTaskVariables(); @@ -119,7 +122,7 @@ export const useChatBoxActions = () => { const triggerTask = useCallback( async (options: TriggerTaskOptions) => { - const { aiEmployee, tasks, variables, localVariables } = options; + const { aiEmployee, tasks } = options; updateRole(aiEmployee); if (!open) { setOpen(true); @@ -147,11 +150,7 @@ export const useChatBoxActions = () => { if (tasks.length === 1) { setMessages(msgs); const task = tasks[0]; - const { userMessage, systemMessage, attachments, workContext } = await parseTask( - task, - variables, - localVariables, - ); + const { userMessage, systemMessage, attachments, workContext } = await parseTask(app, task); if (userMessage && userMessage.type === 'text') { setSenderValue(userMessage.content); } else { diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/utils.ts b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/utils.ts index e7369cde461..2e40293e8e5 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/utils.ts +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/utils.ts @@ -7,8 +7,9 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import { useLocalVariables, useVariables } from '@nocobase/client'; +import { Application, useLocalVariables, useVariables } from '@nocobase/client'; import { ContextItem } from '../types'; +import PluginAIClient from '../..'; async function replaceVariables(template, variables, localVariables = {}) { const regex = /\{\{\s*(.*?)\s*\}\}/g; @@ -53,6 +54,7 @@ async function replaceVariables(template, variables, localVariables = {}) { } export const parseTask = async ( + app: Application, task: { message: { user?: string; @@ -61,26 +63,23 @@ export const parseTask = async ( workContext?: ContextItem[]; }; }, - variables: Record, - localVariables?: Record, ) => { let userMessage: any; const { message } = task; if (message?.user) { - const content = await replaceVariables(message.user, variables, localVariables); userMessage = { type: 'text', - content, + content: message.user, }; } let systemMessage: string; if (message?.system) { - systemMessage = await replaceVariables(message.system, variables, localVariables); + systemMessage = message.system; } const attachments = []; if (message?.attachments?.length) { for (const attachment of message.attachments) { - const obj = await variables?.parseVariable(attachment, localVariables).then(({ value }) => value); + const obj = attachment; if (!obj) { continue; } @@ -97,7 +96,27 @@ export const parseTask = async ( } } } - return { userMessage, systemMessage, attachments, workContext: message.workContext }; + const workContext = []; + if (message.workContext) { + const plugin = app.pm.get('ai') as PluginAIClient; + for (const context of message.workContext) { + if (context.content) { + workContext.push(context); + continue; + } + const contextOptions = plugin.aiManager.workContext.get(context.type); + if (!(contextOptions && contextOptions.getContent)) { + workContext.push(context); + continue; + } + const content = contextOptions.getContent(app, context); + workContext.push({ + ...context, + content, + }); + } + } + return { userMessage, systemMessage, attachments, workContext }; }; const publicPath = window['__nocobase_dev_public_path__'] || window['__nocobase_public_path__'] || '/'; diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/data-modeling.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/data-modeling.tsx deleted file mode 100644 index 807e052cf5c..00000000000 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/data-modeling.tsx +++ /dev/null @@ -1,131 +0,0 @@ -/** - * This file is part of the NocoBase (R) project. - * Copyright (c) 2020-2024 NocoBase Co., Ltd. - * Authors: NocoBase Team. - * - * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. - * For more information, please refer to: https://www.nocobase.com/agreement. - */ - -import React from 'react'; -import { WorkContextOptions } from '../types'; -import { BuildOutlined, StarOutlined, SelectOutlined, DatabaseOutlined } from '@ant-design/icons'; -import { tval } from '@nocobase/utils/client'; -// @ts-ignore -import pkg from '../../../../package.json'; -import { useT } from '../../locale'; -import { useAISelectionContext } from '../selector/AISelectorProvider'; -import { useTranslation } from 'react-i18next'; -import { Schema } from '@formily/react'; -import { Checkbox } from 'antd'; - -export const CollectionDefinitionsContext: WorkContextOptions = { - menu: { - icon: , - label: tval('Modern pages', { ns: pkg.name }), - }, - children: { - // form: { - // menu: { - // icon: , - // Component: ({ onAdd }) => { - // const t = useT(); - // const { t: collectionT } = useTranslation('lm-collections'); - // const { startSelect, ctx } = useAISelectionContext(); - // return ( - //
{ - // startSelect('form', { - // onSelect: ({ uid, fieldSchema, collection }) => { - // if (!uid) { - // return; - // } - // let title = ''; - // if (collection) { - // title = Schema.compile(collection.title, { t: collectionT }); - // } - // onAdd({ - // uid, - // title, - // content: JSON.stringify(fieldSchema.toJSON()), - // }); - // }, - // }); - // }} - // > - // {t('Select form')} - //
- // ); - // }, - // }, - // tag: { - // Component: ({ item }) => { - // const t = useT(); - // return ( - // <> - // {t('Form')} ({item.title}) - // - // ); - // }, - // }, - // actions: [ - // { - // responseType: 'json', - // Component: ({ item, message, value }) => { - // const t = useT(); - // const { ctx } = useAISelectionContext(); - // - // return ( - // - // ); - // }, - // }, - // ], - // }, - users: { - menu: { - Component: ({ onAdd }) => { - const t = useT(); - const { t: collectionT } = useTranslation('lm-collections'); - const { startSelect } = useAISelectionContext(); - return {t('Users #f61f6cda073')}; - }, - }, - tag: { - Component: ({ item }) => { - return ( - <> - {item.title} - - ); - }, - }, - }, - roles: { - menu: { - Component: ({ onAdd }) => { - const t = useT(); - const { startSelect } = useAISelectionContext(); - - return {t('Roles #4cb2eb66a99')}; - }, - }, - }, - }, -}; diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/flow-models.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/flow-models.tsx new file mode 100644 index 00000000000..8bc89b579fb --- /dev/null +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/flow-models.tsx @@ -0,0 +1,71 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import React from 'react'; +import { WorkContextOptions } from '../types'; +import { BuildOutlined, StarOutlined } from '@ant-design/icons'; +import { tval } from '@nocobase/utils/client'; +// @ts-ignore +import pkg from '../../../../package.json'; +import { useFlowEngine } from '@nocobase/flow-engine'; +import _ from 'lodash'; + +function parseModel(node: any) { + if (!node || typeof node !== 'object' || !node.uid || !node.use) return null; + + const parsed = { + uid: node.uid, + use: node.use, + props: node.props || {}, + decoratorProps: node.decoratorProps || {}, + stepParams: node.stepParams || {}, + collectionField: node.collectionField + ? _.pick(node.collectionField, ['dataSourceKey', 'enum', 'name', 'type', 'title', 'uiSchema']) + : {}, + children: {}, + }; + + if (node.subModels && typeof node.subModels === 'object') { + for (const key in node.subModels) { + const group = node.subModels[key]; + if (Array.isArray(group)) { + parsed.children[key] = group.map((sub) => parseModel(sub)).filter(Boolean); + } else if (typeof group === 'object') { + const child = parseModel(group); + if (child) { + parsed.children[key] = [child]; + } + } + } + } + + return parsed; +} + +export const FlowModelsContext: WorkContextOptions = { + name: 'flow-model', + tag: { + Component: ({ item }) => { + const flowEngine = useFlowEngine(); + const model = flowEngine.getModel(item.uid); + return ( + <> + {model.title} + + ); + }, + }, + getContent: (app, { uid }) => { + const model = app.flowEngine.getModel(uid); + if (!model) { + return ''; + } + return JSON.stringify(parseModel(model)); + }, +}; diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutListModel.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutListModel.tsx index ed8da402772..cdb6e181043 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutListModel.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutListModel.tsx @@ -18,24 +18,13 @@ type AIEmployeeShortcutListModelStructure = { }; export class AIEmployeeShortcutListModel extends FlowModel { - // onInit() { - // this.addSubModel('shortcuts', { - // uid: 'data-modeling', - // use: 'AIEmployeeShortcutModel', - // props: { - // avatar: 'notion-3-male', - // }, - // }); - // } - // - // render() { - // return ( - //
- // {this.mapSubModels('shortcuts', (shortcut) => { - // console.log(shortcut.props); - // return ; - // })} - //
- // ); - // } + render() { + return ( +
+ {this.mapSubModels('shortcuts', (shortcut) => { + return ; + })} +
+ ); + } } diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutModel.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutModel.tsx index 367330e12d2..9c9b775662e 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutModel.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/flow/models/AIEmployeeShortcutModel.tsx @@ -7,22 +7,67 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import React from 'react'; -import { Avatar, FloatButton } from 'antd'; +import React, { useMemo, useState } from 'react'; +import { Avatar, Button, Spin, Popover } from 'antd'; import { FlowModel } from '@nocobase/flow-engine'; import { avatars } from '../../avatars'; +import { AIEmployee, TriggerTaskOptions } from '../../types'; +import { useAIEmployeesContext } from '../../AIEmployeesProvider'; +import { useChatBoxActions } from '../../chatbox/hooks/useChatBoxActions'; +import { ProfileCard } from '../../ProfileCard'; + +const Shortcut: React.FC = ({ aiEmployee: { username }, tasks }) => { + const [focus, setFocus] = useState(false); + + const { + aiEmployeesMap, + service: { loading }, + } = useAIEmployeesContext(); + const aiEmployee = aiEmployeesMap[username]; + + const { triggerTask } = useChatBoxActions(); + + const currentAvatar = useMemo(() => { + const avatar = aiEmployee?.avatar; + if (!avatar) { + return null; + } + if (focus) { + return avatars(avatar, { + gesture: ['waveLongArm'], + gestureProbability: 100, + translateX: -15, + }); + } + return avatars(avatar); + }, [aiEmployee, focus]); + + return ( + + } placement="topLeft"> + setFocus(true)} + onMouseLeave={() => setFocus(false)} + onClick={() => { + triggerTask({ aiEmployee, tasks }); + }} + /> + + + ); +}; export class AIEmployeeShortcutModel extends FlowModel { + public declare props: TriggerTaskOptions; + render() { - // const { avatar } = this.props; - // console.log(this.props, this.stepParams, this.getProps()); - // return ( - // } - // style={{ - // insetInlineEnd: '94px', - // }} - // /> - // ); + return ; } } diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/form-filler/tools/index.ts b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/form-filler/tools/index.ts new file mode 100644 index 00000000000..761eb59a6b2 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/form-filler/tools/index.ts @@ -0,0 +1,28 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { FlowEngine } from '@nocobase/flow-engine'; +import { ToolOptions } from '../../../manager/ai-manager'; +import { FormModel } from '@nocobase/client'; + +export const formFillerTool: [string, string, ToolOptions] = [ + 'frontend', + 'formFiller', + { + invoke: (ctx, params) => { + const { form: uid, data } = params; + if (!uid || !data) { + return; + } + const flowEngine = ctx.flowEngine as FlowEngine; + const model = flowEngine.getModel(uid) as FormModel; + model?.form?.setValues(data); + }, + }, +]; diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/icon.png b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/icon.png index d40cf8c92e5629503cc878462ecbef850784516e..1273b3ab8a158491a7e2ff816169415455feb36d 100644 GIT binary patch literal 16422 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 F{{aX?4~75$ delta 18254 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

ICwb1WhOp&%^W4E&Hi z9ue!wyp0m|OKA>X8mNYd+li5pZ*IFk^JG3AI6_t-8eoAa zAfVHvZ#v)mLp@uDU#qgXoZPeT@a9`XR$OLX^R)@knXTh5bQrAf%+3KDyY({WF$4BQ z0JfO-4J+mfhT?#eTJ2+9RqkWM`@^Vqb*%J;`$L0@nGUL`d^HNCQ=6M_LrJ`)ojT#d zu*V;?QcC6x{fB?yK{Y`YPpqXn6?5-~;&7-16a_a*ryc9a zd~bg7`;f0t^E0+YC6_NXIqf=@N4}|Cy8U<-6Wf~KqC>F z@+atBM2eK{G*{05&HxeDssHu5dC!hZNv%cns@p=|S(1Ty(F8sOSsg#uu*dMv=S7n? z&(Ov3V*q;ZfV`VL(&nLZ?)x!hU zmD4k@?RIW%V(Z7YRcg!D9fLJKOAbCs30A@p3agz?=Wirw^G1l|w(bKLEoHl9T{)7Y zm+9rJfUIyDeIVRH;tA zoe-uQvWFuIji9@1_3>p%_px*7&vYQhN5}{{HjwdR3C|{DlfFDXV_a$NoUK{`wC^|g zJitwcugRkV9RPi6KAq;yF8F{*C}%uwhWeCAELa}i;7~*LL_=wr zL>}*xbppI08q5r0Nwu>@Xt*+fvq&x6fh5!+Fz8L%SO!T~+P#ygoK7JONl8iKsG?7S zLf|c4JB%v6Dyc-*f5fk<rStmeBgn5geAwv!(i)lI!|F5VA%**UVG!P|5nd^e7!pq$5vk! zqlYLWEPX&fkm4AN43ybs7*QNlUHhnrCL@zDr0-D&lU0#cMBH?zDyD#0EA^Szr!kdk zISbjjJU#k0c_9P*mWekb2TL;Uu|{qTu6r~jEgAGyzCk0 zOQP@pf*(ncIkt(Q&roQ{_~X>Fb)76IE4nW$Sz3srovj9Y01Sv4(Z5$+F!C7YXU|GL zA&vj{*^m9%G1VW0w(#kV7_4w1yn(W_*bVc13JAjo>i*iQPIR;zX*KS7&|XM@Vd4^* zZIHFJSdT=C6R*)Yd6**!(|;BiNbnR^zrEsvzz!h87yX28Xo$)vDz zp}j~?MWc)$oj+0e-TspzO&MYE4ZC;>MZbV#y>$dUEj9`Ju}f!nFT*O^?AO5c&I7eG zo)9rNRiT?_TtNZeisBoDxHrTclan;o#mpS6cS7w5ZXc`n1e9(doOMA|MG|8##P>%* zbl6dk%z?5*QqMD9;?lklTqvOzk~y(?8;Z!|jjO@kQtznj;)9loF35}%k!WB`gBk8c z)%)rbVWh64nt_hhQI1L>F1|+rf;MUv4Jq}24DSQ`h%h6wQt%oPY1H5Eq$~gU8eb?$ z6F&bcEy5`mMEQI^6+)wx#0yp3AxI%X`WcEnxYbM)-9<9+6As zkrL`7^T^Jw(p~Y$^Zt7CkQi`{v-YU_kkra+e@ctOQ!(Vl>P{v|LK9P)6V^2?PWpL8 z5Fwk<-56(ShX|>E`j$8zWG6^lo~f@P|KRft_o4F>{xRFZhsu4@Uc`{{A{ma4ME?_h z0fGMV#SlZgNKwEzOMc80f++HI1PEb?JZBs#3n0fY`GNU}*g*i)6jOCaC0EV~E98<{ zUyvsW=nygM4eKfmedQ-g(UCY8ihGL?FvGwZP)!5H9UYf6ic^6`f1;idX~4S0i9yAm z``YA1e%7XPG6+*qK;)}J2qR{bG38~sQ7m?bokBwBO?jBB1>s0B)qN^|f=)+Nf`xYZ z?rKIqQGDf^gzqp=e5#ZPw&8lDdfR%0>YIfyC102xMXaV$93) zU&2fgVLdE%L)g?Qf49LtkxX$QC=E=N0LK?*_*M29iBf`TUI8mTF-igej1f}NK!pm7 zWs8mMph)TK&^tOCyDD=fXL}VPWE9A~mIo^D;NVJv?MINX$or!7U^;IkIIN0diZc}i zl}#}#<4 = (props) => { const [selectable, setSelectable] = useState(''); const [selector, setSelector] = useState(null); + const flowEngine = useFlowEngine(); const [ctx, setCtx] = useState<{ [key: string]: Record; - }>({}); + }>({ + flowEngine: flowEngine, + }); const startSelect = (selectType: string, selector?: Selector) => { if (selector) { diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx new file mode 100644 index 00000000000..5bf2c8016ca --- /dev/null +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx @@ -0,0 +1,106 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { Button, Divider } from 'antd'; +import { LeftOutlined, RightOutlined, PlusOutlined } from '@ant-design/icons'; +import { AddSubModelButton, FlowModelRenderer, useFlowEngine } from '@nocobase/flow-engine'; +import React, { useMemo, useState } from 'react'; +import { useShortcuts } from './useShortcuts'; +import { useDesignable, useToken } from '@nocobase/client'; +import { useAIEmployeesContext } from '../AIEmployeesProvider'; +import { AIEmployeeListItem } from '../AIEmployeeListItem'; + +export const ShortcutList: React.FC = () => { + const flowEngine = useFlowEngine(); + const { designable } = useDesignable(); + const { shortcuts, uid, builtIn } = useShortcuts(); + const { token } = useToken(); + const designMode = designable && !builtIn; + + const { + aiEmployees, + service: { loading }, + } = useAIEmployeesContext(); + + const [folded, setFolded] = useState(false); + + const model = useMemo(() => { + return flowEngine.createModel({ + uid: `ai-shortcuts-${uid}`, + use: 'AIEmployeeShortcutListModel', + subModels: { + shortcuts, + }, + }); + }, [flowEngine, shortcuts]); + + return ( + <> + {shortcuts.length > 0 && ( + <> +