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,79 @@
.plugin {
margin-bottom: 0;
.plugin-wrapper {
position: relative;
}
.btn-container {
position: absolute;
bottom: 0;
left: 0;
display: none;
grid-template-columns: repeat(2, minmax(0, 1fr));
width: 100%;
&.one-column-grid {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
}
&:hover {
.btn-container {
display: grid;
}
.description {
visibility: hidden;
}
}
.card-avatar {
width: 48px;
height: 48px;
border-radius: 6px;
/*
* 如此写边框和设置背景色的原因
* 1、边框是内边框处于图片的边缘上因此需要用after来写
* 2、背景色用before实体来写是由于 border和背景色都是透明色重叠会导致颜色加重出现边框
*/
&::after {
content: '';
position: absolute;
z-index: 2;
width: calc(100% - 2px);
height: calc(100% - 2px);
border-style: solid;
border-width: 1px;
border-radius: 6px;
@apply coz-stroke-primary;
}
&::before {
content: '';
position: absolute;
z-index: 1;
top: 1px;
left: 1px;
width: calc(100% - 2px);
height: calc(100% - 2px);
@apply bg-stroke-5;
border-radius: 5px;
}
& > img {
z-index: 2;
}
}
}

View File

@@ -0,0 +1,137 @@
/*
* 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 FC } from 'react';
import cls from 'classnames';
import { type explore } from '@coze-studio/api-schema';
import { I18n } from '@coze-arch/i18n';
import { Avatar, Space, Tag, Toast, Tooltip } from '@coze-arch/coze-design';
import { cozeBaseUrl } from '@/const/url';
import { PluginAuthMode, type AuthMode } from '../type';
import { CardInfo } from '../components/info';
import { CardContainer, CardSkeletonContainer } from '../components/container';
import { CardButton } from '../components/button';
import styles from './index.module.less';
interface ProductInfo extends explore.ProductInfo {
plugin_extra: explore.ProductInfo['plugin_extra'] & AuthMode;
}
export type PluginCardProps = ProductInfo & {
isInstalled?: boolean;
isShowInstallButton?: boolean;
};
export const PluginCard: FC<PluginCardProps> = props => (
<CardContainer
className={styles.plugin}
shadowMode="default"
onClick={() => {
console.log('CardContainer...');
}}
>
<div className={styles['plugin-wrapper']}>
<PluginCardBody {...props} />
<Space
className={cls(styles['btn-container'], {
[styles['one-column-grid']]:
props.isInstalled || !props.isShowInstallButton,
})}
>
{!props.isInstalled && props.isShowInstallButton ? (
<CardButton
onClick={() => {
Toast.success(I18n.t('plugin_install_success'));
}}
>
{I18n.t('plugin_store_install')}
</CardButton>
) : null}
<CardButton
onClick={() => {
window.open(
`${cozeBaseUrl}/store/plugin/${props.meta_info?.id}?from=plugin_card`,
);
}}
>
{I18n.t('plugin_usage_limits_modal_view_details')}
</CardButton>
</Space>
</div>
</CardContainer>
);
export const PluginCardSkeleton = () => (
<CardSkeletonContainer className={cls('h-[186px]', styles.plugin)} />
);
const PluginCardBody: FC<PluginCardProps> = props => {
const renderCardTag = () => {
if (
props.plugin_extra.auth_mode === PluginAuthMode.Required ||
props.plugin_extra.auth_mode === PluginAuthMode.Supported
) {
return (
<Tag
color={'yellow'}
className="h-[20px] !px-[4px] !py-[2px] coz-fg-primary font-medium shrink-0"
>
<span className="ml-[2px]">
{I18n.t('plugin_store_unauthorized')}
</span>
</Tag>
);
} else if (props.plugin_extra.auth_mode === PluginAuthMode.Configured) {
return (
<Tooltip content={I18n.t('plugin_store_contact_deployer')}>
<Tag
color={'brand'}
className="h-[20px] !px-[4px] !py-[2px] coz-fg-primary font-medium shrink-0"
>
<span className="ml-[2px]">
{I18n.t('plugin_store_authorized')}
</span>
</Tag>
</Tooltip>
);
}
return null;
};
return (
<div>
<Avatar
className={styles['card-avatar']}
src={props.meta_info?.icon_url}
shape="square"
/>
<CardInfo
{...{
title: props.meta_info?.name,
description: props.meta_info?.description,
userInfo: props.meta_info?.user_info,
authMode: props.plugin_extra.auth_mode,
renderCardTag,
descClassName: styles.description,
}}
/>
</div>
);
};