feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
.main {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.sider {
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
width: 218px;
|
||||
padding: 12px;
|
||||
|
||||
background-color: #ebedf0;
|
||||
}
|
||||
|
||||
.content {
|
||||
overflow-y: hidden;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
|
||||
height: 100%;
|
||||
|
||||
background: #f7f7fa;
|
||||
|
||||
.filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding: 8px 24px;
|
||||
}
|
||||
|
||||
.content-inner {
|
||||
overflow-y: hidden;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.data-sets-content {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @coze-arch/max-line-per-function */
|
||||
import classNames from 'classnames';
|
||||
import { type ModalProps } from '@douyinfe/semi-foundation/lib/es/modal/modalFoundation';
|
||||
import { useWorkflowModalParts } from '@coze-workflow/components';
|
||||
import { type WorkFlowItemType } from '@coze-studio/bot-detail-store';
|
||||
import { KnowledgeListModalContent } from '@coze-data/knowledge-modal-adapter';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { UITabsModal } from '@coze-arch/bot-semi';
|
||||
import { PageType, SceneType, usePageJumpResponse } from '@coze-arch/bot-hooks';
|
||||
import { type Dataset } from '@coze-arch/bot-api/knowledge';
|
||||
import { type PluginApi, WorkflowMode } from '@coze-arch/bot-api/developer_api';
|
||||
import { type PluginModalModeProps } from '@coze-agent-ide/plugin-shared';
|
||||
import { usePluginModalParts } from '@coze-agent-ide/bot-plugin-export/agentSkillPluginModal/hooks';
|
||||
|
||||
import { isNonNull } from './utils';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
export interface SkillsModalProps
|
||||
extends Partial<ModalProps>,
|
||||
PluginModalModeProps {
|
||||
tabs: ('plugin' | 'workflow' | 'datasets' | 'imageFlow')[];
|
||||
tabsConfig?: {
|
||||
plugin?: {
|
||||
list: PluginApi[];
|
||||
onChange: (list: PluginApi[]) => void;
|
||||
};
|
||||
workflow?: {
|
||||
list: WorkFlowItemType[];
|
||||
onChange: (list: WorkFlowItemType[]) => void;
|
||||
};
|
||||
datasets?: {
|
||||
list: Dataset[];
|
||||
onChange: (list: Dataset[]) => void;
|
||||
};
|
||||
imageFlow?: {
|
||||
list: WorkFlowItemType[];
|
||||
onChange: (list: WorkFlowItemType[]) => void;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const SCENE_TAB_MAP: Partial<
|
||||
Record<SceneType, 'tools' | 'workflow' | 'datasets' | 'imageFlow'>
|
||||
> = {
|
||||
[SceneType.WORKFLOW__BACK__BOT]: 'workflow',
|
||||
};
|
||||
|
||||
export function SkillsModal({
|
||||
tabsConfig,
|
||||
openMode,
|
||||
openModeCallback,
|
||||
tabs,
|
||||
...restModalProps
|
||||
}: SkillsModalProps) {
|
||||
const {
|
||||
plugin: {
|
||||
list: pluginApiList = [],
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onChange: onPluginApiListChange = () => {},
|
||||
} = {},
|
||||
workflow: {
|
||||
list: workFlowList = [],
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onChange: onWorkFlowListChange = () => {},
|
||||
} = {},
|
||||
datasets: {
|
||||
list: datasetList = [],
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onChange: onDatasetListChange = () => {},
|
||||
} = {},
|
||||
imageFlow: {
|
||||
list: imageFlowList = [],
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onChange: onImageFlowListChange = () => {},
|
||||
} = {},
|
||||
} = tabsConfig ?? {};
|
||||
const jumpResponse = usePageJumpResponse(PageType.BOT);
|
||||
const pluginModalParts = usePluginModalParts({
|
||||
pluginApiList,
|
||||
onPluginApiListChange,
|
||||
openModeCallback,
|
||||
openMode,
|
||||
});
|
||||
|
||||
const workflowModalParts = useWorkflowModalParts({
|
||||
workFlowList,
|
||||
onWorkFlowListChange,
|
||||
onAdd: openModeCallback,
|
||||
});
|
||||
|
||||
const imageFlowModalParts = useWorkflowModalParts({
|
||||
// 如果是仅添加一次/或者添加多次,清空默认选中
|
||||
workFlowList: imageFlowList,
|
||||
onWorkFlowListChange: onImageFlowListChange,
|
||||
flowMode: WorkflowMode.Imageflow,
|
||||
onAdd: openModeCallback,
|
||||
});
|
||||
|
||||
const toolTabPane = {
|
||||
tabPaneProps: {
|
||||
tab: I18n.t('Tools'),
|
||||
itemKey: 'tools',
|
||||
},
|
||||
content: (
|
||||
<div className={s.main}>
|
||||
<div className={s.sider}>{pluginModalParts.sider}</div>
|
||||
<div className={s.content}>
|
||||
<div className={s.filter}>{pluginModalParts.filter}</div>
|
||||
<div className={s['content-inner']}>{pluginModalParts.content}</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
const workflowTabPane = {
|
||||
tabPaneProps: {
|
||||
tab: I18n.t('Workflow'),
|
||||
itemKey: 'workflow',
|
||||
},
|
||||
content: (
|
||||
<div className={s.main}>
|
||||
<div className={s.sider}>{workflowModalParts.sider}</div>
|
||||
<div className={s.content}>
|
||||
{!!workflowModalParts.filter && (
|
||||
<div className={s.filter}>{workflowModalParts.filter}</div>
|
||||
)}
|
||||
<div className={s['content-inner']}>{workflowModalParts.content}</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
const datasetTabPane = {
|
||||
tabPaneProps: {
|
||||
tab: I18n.t('Datasets'),
|
||||
itemKey: 'datasets',
|
||||
},
|
||||
content: (
|
||||
<div className={classNames(s.main, s['data-sets-content'])}>
|
||||
<KnowledgeListModalContent
|
||||
datasetList={datasetList}
|
||||
onDatasetListChange={onDatasetListChange}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
const imageFlowTabPane = {
|
||||
tabPaneProps: {
|
||||
tab: I18n.t('imageflow_title'),
|
||||
itemKey: 'imageFlow',
|
||||
},
|
||||
content: (
|
||||
<div className={s.main}>
|
||||
<div className={s.sider}>{imageFlowModalParts.sider}</div>
|
||||
<div className={s.content}>
|
||||
{!!imageFlowModalParts.filter && (
|
||||
<div className={s.filter}>{imageFlowModalParts.filter}</div>
|
||||
)}
|
||||
<div className={s['content-inner']}>
|
||||
{imageFlowModalParts.content}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
<UITabsModal
|
||||
visible
|
||||
tabs={{
|
||||
tabsProps: {
|
||||
lazyRender: true,
|
||||
defaultActiveKey: jumpResponse?.scene
|
||||
? SCENE_TAB_MAP[jumpResponse.scene] || 'tools'
|
||||
: 'tools',
|
||||
},
|
||||
tabPanes: tabs
|
||||
.map(tab => {
|
||||
if (tab === 'plugin') {
|
||||
return toolTabPane;
|
||||
}
|
||||
if (tab === 'workflow') {
|
||||
return workflowTabPane;
|
||||
}
|
||||
if (tab === 'datasets') {
|
||||
return datasetTabPane;
|
||||
}
|
||||
if (tab === 'imageFlow') {
|
||||
return imageFlowTabPane;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(isNonNull),
|
||||
}}
|
||||
{...restModalProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: 后续添加返会新增skill需要用到
|
||||
// export const useSkillsModal = (props: SkillsModalProps) => {
|
||||
// const pageJumpResp = usePageJumpResponse(PageType.BOT);
|
||||
// // const [visible, setVisible] = useState(
|
||||
// // pageJumpResp?.scene === SceneType.WORKFLOW__BACK__BOT &&
|
||||
// // pageJumpResp.agentID === agentId &&
|
||||
// // !!pageJumpResp.workflowModalState,
|
||||
// // );
|
||||
// const [visible, setVisible] = useState(false);
|
||||
// const close = () => {
|
||||
// setVisible(false);
|
||||
// };
|
||||
// const open = () => {
|
||||
// setVisible(true);
|
||||
// };
|
||||
// useEffect(() => {
|
||||
// if (visible) {
|
||||
// pageJumpResp?.clearScene();
|
||||
// }
|
||||
// }, []);
|
||||
// return {
|
||||
// node: visible ? <SkillsModal {...props} onCancel={close} /> : null,
|
||||
// close,
|
||||
// open,
|
||||
// };
|
||||
// };
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export function isNonNull<T>(value: T | null): value is T {
|
||||
return value !== null;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { SheetView } from '@coze-agent-ide/space-bot/component';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconBotMultiLeftBtnIcon } from '@coze-arch/bot-icons';
|
||||
import { type BotMode } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import s from '../../pages/index.module.less';
|
||||
|
||||
interface ToolSheetProps {
|
||||
mode: BotMode;
|
||||
titleNode: ReactNode;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const ToolSheet = ({ mode, titleNode, children }: ToolSheetProps) => (
|
||||
<SheetView
|
||||
headerClassName={classNames([
|
||||
'coz-bg-plus',
|
||||
'coz-fg-secondary',
|
||||
s['sheet-view-left-header'],
|
||||
s['sheet-view-new-header'],
|
||||
])}
|
||||
mode={mode}
|
||||
title={I18n.t('bot_build_title')}
|
||||
titleNode={titleNode}
|
||||
slideProps={{
|
||||
placement: 'left',
|
||||
closeBtnTooltip: I18n.t('chatflow_develop_tooltip_hide'),
|
||||
openBtnTooltip: I18n.t('chatflow_develop_tooltip_show'),
|
||||
width: 400,
|
||||
visible: true,
|
||||
btnNode: <IconBotMultiLeftBtnIcon />,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</SheetView>
|
||||
);
|
||||
Reference in New Issue
Block a user