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,293 @@
/*
* 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 { useState } from 'react';
import { sortBy } from 'lodash-es';
import classNames from 'classnames';
import { useBoolean } from 'ahooks';
import { type WorkflowNodeJSON } from '@flowgram-adapter/free-layout-editor';
import { ParametersPopover } from '@coze-studio/components/parameters-popover';
import { CardThumbnailPopover } from '@coze-studio/components';
import { I18n } from '@coze-arch/i18n';
import {
Space,
Tooltip,
Typography,
UIButton,
UITag,
} from '@coze-arch/bot-semi';
import {
ProductStatus,
type public_api,
} from '@coze-arch/bot-api/product_api';
import { type PluginApi } from '@coze-arch/bot-api/plugin_develop';
import { useViewExample } from '@coze-agent-ide/bot-plugin-tools/useViewExample';
import { Popconfirm } from '@coze-arch/coze-design';
import { OverflowList } from '@blueprintjs/core';
import { From } from '../../types/plugin-modal-types';
import { PluginPerfStatics } from './plugin-perf-statics';
import s from './index.module.less';
type PluginToolInfo = public_api.PluginToolInfo;
export interface PluginItemProps {
isAdded?: boolean;
onApiToggle: () => Promise<boolean>;
pluginApi: PluginApi;
showButton?: boolean;
marketStatus?: ProductStatus;
from?: From;
workflowNodes?: WorkflowNodeJSON[];
loading?: boolean;
marketPluginInfo?: PluginToolInfo;
isLocalPlugin?: boolean;
connectors?: string[];
}
interface OverflowTagItem {
tagName?: string;
key?: string;
}
// eslint-disable-next-line complexity
export const PluginItem: React.FC<PluginItemProps> = ({
isAdded,
onApiToggle,
pluginApi,
marketStatus,
showButton,
from,
workflowNodes,
loading,
marketPluginInfo,
isLocalPlugin,
connectors,
}) => {
const { name, desc, parameters, debug_example } = pluginApi;
const { exampleNode, doShowExample } = useViewExample();
const [isMouseIn, { setFalse, setTrue }] = useBoolean(false);
const onMouseEnter = () => {
setTrue();
};
const onMouseLeave = () => {
setFalse();
};
const [count, setCount] = useState((workflowNodes || []).length);
const isFromWorkflow =
from === From.WorkflowAddNode || from === From.ProjectWorkflow;
const renderOverflow = (items: OverflowTagItem[]) =>
items.length ? (
<UITag className={s['tag-item']} size="small">
+{items.length}
</UITag>
) : null;
const renderItem = (item: OverflowTagItem) => (
<UITag className={s['tag-item']} size="small" key={item.key}>
{item.tagName}
</UITag>
);
const isDisabled = marketStatus === ProductStatus?.Unlisted;
// 端插件且未添加过 提示适用渠道
const showAddConfirm =
isLocalPlugin &&
((!isFromWorkflow && !isAdded) || (isFromWorkflow && count === 0));
return (
<>
{exampleNode}
<div className={s['plugin-item']}>
<div className={s['plugin-api-main']}>
<Space className={s['plugin-api-name']}>
<Typography.Text
ellipsis={{
showTooltip: {
opts: {
content: name,
style: { wordBreak: 'break-word' },
},
},
}}
>
{name}
</Typography.Text>
{/* 预览预置卡片 */}
{pluginApi?.card_binding_info?.thumbnail ? (
<CardThumbnailPopover
url={pluginApi?.card_binding_info?.thumbnail}
/>
) : null}
</Space>
<div className={s['plugin-api-desc']}>
<Typography.Text
style={{ width: 640 }}
ellipsis={{
showTooltip: {
opts: {
content: desc,
style: { wordBreak: 'break-word', maxWidth: '560px' },
},
},
rows: 1,
}}
>
{desc}
</Typography.Text>
{!!parameters?.length && (
<div className={s['api-params']}>
<OverflowList
items={sortBy(
parameters,
item => item.name?.length,
)?.map<OverflowTagItem>((tag, index) => ({
tagName: tag.name,
key: String(index),
}))}
overflowRenderer={renderOverflow}
visibleItemRenderer={renderItem}
collapseFrom="end"
className={s['params-tags']}
/>
<ParametersPopover pluginApi={pluginApi}>
<div className={s['params-desc']}>{I18n.t('parameters')}</div>
</ParametersPopover>
{debug_example ? (
<div
className={s['params-desc']}
style={{ marginLeft: '8px' }}
onClick={() =>
doShowExample({
scene: 'bot',
requestParams: parameters,
debugExample: debug_example,
})
}
>
{I18n.t('plugin_edit_tool_view_example')}
</div>
) : null}
</div>
)}
<PluginPerfStatics
className={s['store-plugin-tools']}
callAmount={marketPluginInfo?.call_amount}
avgExecTime={marketPluginInfo?.avg_exec_time}
successRate={marketPluginInfo?.success_rate}
botsUseCount={marketPluginInfo?.bots_use_count}
/>
</div>
</div>
<div className={s['plugin-api-method']}>
{showButton ? (
<Popconfirm
key={`${showAddConfirm}`}
trigger={!isDisabled && showAddConfirm ? 'click' : 'custom'}
position="bottomRight"
title={I18n.t('store_service_plugin_connector_only', {
connector_names: connectors?.join('、'),
})}
okText={I18n.t('Add_1')}
cancelText={I18n.t('Cancel')}
onConfirm={() => {
onApiToggle?.().then(isSuccess => {
if (isSuccess) {
setCount(prev => prev + 1);
}
});
}}
>
<div>
<Tooltip
content={I18n.t('mkpl_plugin_delisted_tips')}
trigger={isDisabled ? 'hover' : 'custom'}
>
<UIButton
data-testid="bot.ide.plugin.plugin-panel-plugin-item-btn"
className={classNames(s['operator-btn'], {
[s.added]: !isDisabled && isAdded,
[s.addedMouseIn]: !isDisabled && isAdded && isMouseIn,
})}
onClick={() => {
if (showAddConfirm) {
return;
}
onApiToggle?.().then(isSuccess => {
if (isSuccess) {
setCount(prev => prev + 1);
}
});
}}
onMouseEnter={onMouseEnter}
disabled={isDisabled}
loading={loading && isFromWorkflow}
onMouseLeave={onMouseLeave}
>
{isAdded && !isDisabled ? (
isMouseIn ? (
I18n.t('Remove')
) : (
I18n.t('Added')
)
) : (
<>
<span>{I18n.t('Add_1')}</span>
{isFromWorkflow && count !== 0 ? (
<span className={s.workflow_count_span}>{count}</span>
) : null}
</>
)}
</UIButton>
</Tooltip>
</div>
</Popconfirm>
) : null}
</div>
{/* <Switch
disabled={isReadonly}
checked={Boolean(
$enabledPluginApiList.apiList.find(
api =>
(api.plugin_id?.toString() ?? '0') + (api.name ?? '') ===
(plugin_id?.toString() ?? '0') + (name ?? ''),
),
)}
onChange={checked => {
if (!checked) {
const index = $enabledPluginApiList.apiList.findIndex(
api =>
(api.plugin_id?.toString() ?? '0') + (api.name ?? '') ===
(plugin_id?.toString() ?? '0') + (name ?? ''),
);
if (index >= 0) {
$enabledPluginApiList.apiList.splice(index, 1);
}
return;
}
$enabledPluginApiList.apiList.push(pluginApi);
}}
/> */}
</div>
</>
);
};