feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -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;
}

View File

@@ -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,
// };
// };

View File

@@ -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;
}

View File

@@ -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>
);