chore: replace all cn comments of fe to en version by volc api (#320)
This commit is contained in:
@@ -77,10 +77,10 @@ describe('SelectSpaceModal', () => {
|
||||
wrapper.getByDisplayValue('mockBot(duplicate_rename_copy)'),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// 检查表单是否存在
|
||||
// Check if the form exists
|
||||
expect(wrapper.getByRole('form')).toBeInTheDocument();
|
||||
|
||||
// 检查确定和取消按钮
|
||||
// Check OK and Cancel buttons
|
||||
expect(
|
||||
wrapper.getByRole('button', { name: 'confirm' }),
|
||||
).toBeInTheDocument();
|
||||
|
||||
@@ -28,23 +28,23 @@ import { CarouselItem } from './carousel-item';
|
||||
import styles from './index.module.less';
|
||||
|
||||
export interface CarouselProps {
|
||||
/** 元素布局行数默认为1 */
|
||||
/** The number of rows in the element layout defaults to 1. */
|
||||
rows?: number;
|
||||
/** 元素布局列数,默认为均分数组 */
|
||||
/** Number of element layout columns, the default is to divide the array equally */
|
||||
column?: number;
|
||||
/** 每次点击箭头滚动的百分比,0~1. 默认值为0.5 */
|
||||
/** Percentage of scrolling per click of arrow, 0~ 1. Default value is 0.5 */
|
||||
scrollStep?: number;
|
||||
/** 滚动回调 */
|
||||
/** scrolling callback */
|
||||
onScroll?: () => void;
|
||||
/** 箭头是否显示边框 */
|
||||
/** Whether the arrow shows the border */
|
||||
enableArrowBorder?: boolean;
|
||||
/** 箭头是否显示阴影渐变 */
|
||||
/** Whether the arrows show gradual changes in shadows */
|
||||
enableArrowShalldow?: boolean;
|
||||
/** 子元素样式 */
|
||||
/** child style */
|
||||
itemClassName?: string;
|
||||
/** 左箭头样式 */
|
||||
/** Left Arrow Style */
|
||||
leftArrowClassName?: string;
|
||||
/** 右箭头样式 */
|
||||
/** Right Arrow Style */
|
||||
rightArrowClassName?: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
@@ -156,7 +156,7 @@ export const Carousel: React.FC<CarouselProps> = ({
|
||||
itemsContainerRef?.current?.scrollLeft !== undefined &&
|
||||
itemsContainerRef?.current?.clientWidth
|
||||
) {
|
||||
// 部分浏览器不支持 scrollTo 方法
|
||||
// Some browsers do not support the scrollTo method
|
||||
itemsContainerRef.current.scrollTo?.({
|
||||
left: Math.max(
|
||||
itemsContainerRef.current.scrollLeft -
|
||||
@@ -193,7 +193,7 @@ export const Carousel: React.FC<CarouselProps> = ({
|
||||
scrollLeft;
|
||||
|
||||
const shouldShowArrowLeft = scrollLeft > 0;
|
||||
// 极端场景下存在 1px 偏差
|
||||
// There is a 1px bias in extreme scenarios
|
||||
const shouldShowArrowRight = Math.abs(scrollRight) > 2;
|
||||
|
||||
setLeftArrowVisible(shouldShowArrowLeft);
|
||||
@@ -204,7 +204,7 @@ export const Carousel: React.FC<CarouselProps> = ({
|
||||
updateArrowVisible();
|
||||
};
|
||||
|
||||
// 初始化时判读一次是否显示箭头
|
||||
// Determine whether to display arrows once during initialization
|
||||
updateArrowVisible();
|
||||
itemsContainerRef?.current?.addEventListener('scroll', scrollEvent);
|
||||
window?.addEventListener('resize', updateArrowVisible);
|
||||
|
||||
@@ -39,7 +39,7 @@ interface CollapsibleIconButtonContextValue {
|
||||
setItems: Dispatch<SetStateAction<ContextItems | undefined>>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- 这是 context
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is context
|
||||
export const CollapsibleIconButtonContext =
|
||||
createContext<CollapsibleIconButtonContextValue>({
|
||||
showText: true,
|
||||
@@ -56,7 +56,7 @@ export const useItem = (key: symbol, ref: RefObject<HTMLElement>) => {
|
||||
}));
|
||||
}, [size?.width]);
|
||||
|
||||
// 组件销毁后移除
|
||||
// Component destruction and removal
|
||||
useEffect(
|
||||
() => () => {
|
||||
setItems(items => omit(items, key));
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
|
||||
import { CollapsibleIconButtonContext, useWrapper, useItem } from './context';
|
||||
|
||||
/** 能让 Group 内的所有 CollapsibleIconButton 根据空余宽度自动展开(露出文案)收起(隐藏文案只剩图标) */
|
||||
/** Can make all the CollapsibleIconButton in the Group automatically expand according to the free width (expose the copy) and put away (hide the copy and only the icon is left) */
|
||||
export const CollapsibleIconButtonGroup: FC<
|
||||
PropsWithChildren<{
|
||||
/** @default 12 */
|
||||
@@ -67,12 +67,12 @@ export const CollapsibleIconButton = forwardRef<
|
||||
|
||||
return (
|
||||
<span ref={ref}>
|
||||
{/* 不可见时渲染到屏幕外侧,用于获取宽度 */}
|
||||
{/* Render to the outside of the screen when not visible for width */}
|
||||
<div className={showText ? '' : 'fixed left-[-999px]'} ref={contentRef}>
|
||||
<Button
|
||||
size="default"
|
||||
color="secondary"
|
||||
// 不可见时不附带 testid,避免对 E2E 产生影响
|
||||
// Invisible without testid to avoid impact on E2E
|
||||
{...(showText ? rest : omit(rest, 'data-testid'))}
|
||||
>
|
||||
{text}
|
||||
@@ -87,7 +87,7 @@ export const CollapsibleIconButton = forwardRef<
|
||||
);
|
||||
});
|
||||
|
||||
/** 更为通用的版本 */
|
||||
/** A more general version */
|
||||
export const Collapsible = forwardRef<
|
||||
HTMLSpanElement,
|
||||
{
|
||||
@@ -102,7 +102,7 @@ export const Collapsible = forwardRef<
|
||||
|
||||
return (
|
||||
<span ref={ref}>
|
||||
{/* 不可见时渲染到屏幕外侧,用于获取宽度 */}
|
||||
{/* Render to the outside of the screen when not visible for width */}
|
||||
<div className={showFull ? '' : 'fixed left-[-999px]'} ref={contentRef}>
|
||||
{fullContent}
|
||||
</div>
|
||||
@@ -118,7 +118,7 @@ export const Collapsible = forwardRef<
|
||||
);
|
||||
});
|
||||
|
||||
/** 不会折叠,但参与宽度计算的元素 */
|
||||
/** Elements that do not collapse, but participate in width calculations */
|
||||
export function PlaceholderContainer({
|
||||
itemKey,
|
||||
children,
|
||||
|
||||
@@ -57,7 +57,7 @@ interface DuplicateBotProps {
|
||||
buttonSize?: Size;
|
||||
enableCozeDesign?: boolean;
|
||||
/**
|
||||
* cozeDesign 的情况下才生效
|
||||
* CozeDesign only takes effect in the case of
|
||||
*/
|
||||
isBlock?: boolean;
|
||||
eventCallbacks?: Partial<{
|
||||
@@ -143,17 +143,17 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
|
||||
});
|
||||
}
|
||||
|
||||
//复制完成,关闭空间弹窗
|
||||
//Copy complete, close the space pop-up window
|
||||
setShowSpaceModal(false);
|
||||
} else if (pageFromFromStore === BotPageFromEnum.Explore) {
|
||||
//explore时可以复制到某个空间下
|
||||
//When exploring, it can be copied to a certain space
|
||||
resp = await DeveloperApi.DuplicateBotToSpace({
|
||||
draft_bot_id: botIdFromStore,
|
||||
target_space_id: targetSpaceId || '',
|
||||
name,
|
||||
});
|
||||
|
||||
//复制完成,关闭空间弹窗
|
||||
//Copy complete, close the space pop-up window
|
||||
setShowSpaceModal(false);
|
||||
} else {
|
||||
resp = await SpaceApi.DuplicateDraftBot({
|
||||
@@ -260,14 +260,14 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
|
||||
from: 'explore_card',
|
||||
source: 'explore_bot_detailpage',
|
||||
});
|
||||
//探索页面来源:team>1时选择copy 空间,否则copy到个人空间
|
||||
//Explore the page Source: Select copy space when team > 1, otherwise copy to personal space
|
||||
if (list.length === 1) {
|
||||
openNewWindow(() => copyAndOpenBot(list?.[0].id));
|
||||
} else {
|
||||
setShowSpaceModal(true);
|
||||
}
|
||||
} else if (pageFrom === BotPageFromEnum.Template) {
|
||||
//探索页面来源:team>1时选择copy 空间,否则copy到个人空间
|
||||
//Explore the page Source: Select copy space when team > 1, otherwise copy to personal space
|
||||
if (list.length === 1) {
|
||||
openNewWindow(() => copyAndOpenBot(list?.[0].id));
|
||||
} else {
|
||||
@@ -281,7 +281,7 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
|
||||
source: 'bots_detailpage',
|
||||
from: 'bots_card',
|
||||
});
|
||||
// bot页面来源:若有操作权限直接copy到当前空间下
|
||||
// Bot page source: If there is operation permission, directly copy it to the current space
|
||||
if (hide_operation) {
|
||||
Toast.warning('Bot in public space cannot duplicate');
|
||||
return;
|
||||
@@ -316,7 +316,7 @@ export const DuplicateBot: FC<DuplicateBotProps> = ({
|
||||
</UIButton>
|
||||
)}
|
||||
|
||||
{/* 选择空间弹窗 */}
|
||||
{/* Select space pop-up */}
|
||||
<SelectSpaceModal
|
||||
botName={botName ?? botNameFromStore}
|
||||
visible={showSpaceModal}
|
||||
|
||||
@@ -93,7 +93,7 @@ export const GenerateButton: React.FC<GenerateButtonProps> = ({
|
||||
(scene === 'gif' && gifCount >= 10) ||
|
||||
(scene === 'static_image' && staticImageCount >= 20)
|
||||
) {
|
||||
// 达到上限,禁用按钮
|
||||
// Limit reached, disable button
|
||||
setExceedImageGenCountLimit(true);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ export const GenerateButton: React.FC<GenerateButtonProps> = ({
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
// 获取图片限制,每天限制10个gif,20个静态图,根据scene来判断是否达到上限
|
||||
// Get the picture limit, limit 10 gifs and 20 static pictures per day, and judge whether the upper limit is reached according to the scene.
|
||||
if (!loading) {
|
||||
getGenPicTimes();
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export default function ImagePicker(props: ImagePickerProps) {
|
||||
maxSize={2 * 1024}
|
||||
onSizeError={() => {
|
||||
Toast.error({
|
||||
// starling 切换
|
||||
// Starling toggle
|
||||
content: withSlardarIdButton(
|
||||
I18n.t(
|
||||
'dataset_upload_image_warning',
|
||||
|
||||
@@ -35,11 +35,11 @@ import s from './index.module.less';
|
||||
|
||||
interface GenerateGifProps {
|
||||
scene: 'background' | 'avatar';
|
||||
image: ImageItem; // 默认图片
|
||||
text: string; // 默认文本
|
||||
loading: boolean; // 生成时,socket全局监听服务端响应,因此loading需要受控
|
||||
imageList?: ImageItem[]; // 图片候选列表(只包含静态图)
|
||||
generatingTaskId: string; // 生成中的任务id
|
||||
image: ImageItem; // default image
|
||||
text: string; // default text
|
||||
loading: boolean; // When generated, sockets listen globally for server level responses, so loading needs to be controlled
|
||||
imageList?: ImageItem[]; // Picture Candidate List (contains only static images)
|
||||
generatingTaskId: string; // Generating task id
|
||||
exceedMaxImageCount?: boolean;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
@@ -106,7 +106,7 @@ export const GenerateGif: React.FC<GenerateGifProps> = ({
|
||||
setLoading?.(true);
|
||||
try {
|
||||
avatarBackgroundWebSocket.createConnection();
|
||||
// 只负责发送请求,响应在WebSocket中接收
|
||||
// Only responsible for sending requests, responses are received in WebSocket
|
||||
const { data } = await PlaygroundApi.GeneratePic({
|
||||
gen_prompt: {
|
||||
ori_prompt: text.trim(),
|
||||
|
||||
@@ -31,15 +31,15 @@ import { type TabItem } from './type';
|
||||
import s from './index.module.less';
|
||||
|
||||
export interface GenerateImageTabProps {
|
||||
// tab列表
|
||||
// tab list
|
||||
tabs: TabItem[];
|
||||
// 是否可折叠
|
||||
// Is it foldable?
|
||||
enableCollapsible?: boolean;
|
||||
// 当前激活的tab
|
||||
// Currently active tab
|
||||
activeKey?: string;
|
||||
// 当前激活的tab变化回调
|
||||
// Currently active tab change callback
|
||||
onTabChange?: (tabKey: string) => void;
|
||||
// 是否展示wait文案
|
||||
// Whether to show the waiting copy
|
||||
showWaitTip?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export const GenerateImageTab: React.FC<GenerateImageTabProps> = ({
|
||||
setOpen(!isOpen);
|
||||
};
|
||||
|
||||
// tabPane 不卸载
|
||||
// tabPane does not uninstall
|
||||
const component = (
|
||||
<div>
|
||||
{tabs.map(item => (
|
||||
@@ -124,7 +124,7 @@ export const GenerateImageTab: React.FC<GenerateImageTabProps> = ({
|
||||
) : null}
|
||||
</div>
|
||||
{enableCollapsible ? (
|
||||
// keepDOM 异常失效使用collapseHeight 不销毁dom保留状态
|
||||
// keepDOM exception fails using collapseHeight does not destroy dom keep state
|
||||
<Collapsible isOpen={isOpen} keepDOM collapseHeight={1}>
|
||||
<div> {component} </div>
|
||||
</Collapsible>
|
||||
|
||||
@@ -28,8 +28,8 @@ import s from './index.module.less';
|
||||
export type ImageItem = PicTask;
|
||||
|
||||
export interface ImageListProps {
|
||||
selectedKey?: string; // 选中的key
|
||||
data: ImageItem[]; // 列表数据
|
||||
selectedKey?: string; // Selected key
|
||||
data: ImageItem[]; // list data
|
||||
className?: string;
|
||||
imageItemClassName?: string;
|
||||
showDeleteIcon?: boolean;
|
||||
@@ -39,18 +39,18 @@ export interface ImageListProps {
|
||||
index?: number;
|
||||
item?: ImageItem;
|
||||
data: ImageItem[];
|
||||
}) => void; // 删除图片,data是此次删除之后的数据
|
||||
}) => void; // Delete the picture, data is the data after this deletion.
|
||||
onSelect?: (params: {
|
||||
index?: number;
|
||||
item: ImageItem;
|
||||
data: ImageItem[];
|
||||
selected: boolean;
|
||||
}) => void; // 选中图片,其中item和data都是此次选中之前的数据,selected表示在本次选中之前此图片是否已是选中状态
|
||||
}) => void; // Select the picture, where item and data are the data before this selection. Selected indicates whether the picture was selected before this selection
|
||||
onClick?: (params: {
|
||||
index: number;
|
||||
item: ImageItem;
|
||||
data: ImageItem[];
|
||||
}) => void; // 点击图片
|
||||
}) => void; // Click on the picture.
|
||||
}
|
||||
|
||||
export const ImageList: React.FC<ImageListProps> = ({
|
||||
|
||||
@@ -56,7 +56,7 @@ export { UploadGenerateButton } from './upload-generate-button';
|
||||
|
||||
export { usePluginLimitModal, transPricingRules } from './plugin-limit-info';
|
||||
|
||||
// 曝光埋点上报组件,进入视图上报
|
||||
// Exposure event tracking report component, enter the view report
|
||||
export { TeaExposure } from './tea-exposure';
|
||||
export { Sticky } from './sticky';
|
||||
|
||||
@@ -67,6 +67,6 @@ export {
|
||||
appendCopySuffix,
|
||||
} from './project-duplicate-modal';
|
||||
export { SpaceFormSelect } from './space-form-select';
|
||||
// !Notice 以下模块只允许导出类型,避免首屏加载 react-dnd,@blueprintjs/core 等相关代码
|
||||
// ! Notice that the following modules only allow export types, avoid loading react-dnd, @blueprintjs/core and other related codes on the first screen
|
||||
export { type TItemRender, type ITemRenderProps } from './sortable-list';
|
||||
export { type ConnectDnd, type OnMove } from './sortable-list/hooks';
|
||||
|
||||
@@ -109,7 +109,7 @@ export const InputSlider: React.FC<InputSliderProps> = ({
|
||||
const onNumberChange = (numberValue: number) => {
|
||||
updateInputNumber();
|
||||
|
||||
// 防止 -0
|
||||
// Prevent -0
|
||||
if (numberValue === 0) {
|
||||
onChange?.(0);
|
||||
return;
|
||||
@@ -124,7 +124,7 @@ export const InputSlider: React.FC<InputSliderProps> = ({
|
||||
onChange?.(expectedFormattedValue);
|
||||
};
|
||||
|
||||
// 防止 -0 导致 InputNumber 无限循环更新
|
||||
// Prevent -0 from causing InputNumber to update indefinitely
|
||||
const fixedValue = Object.is(value, -0) ? 0 : value;
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -36,7 +36,7 @@ const LimitCount: React.FC<LimitCountProps> = ({ maxLen, len }) => (
|
||||
);
|
||||
|
||||
export interface InputWithCountProps extends InputProps {
|
||||
// 设置字数限制并显示字数统计
|
||||
// Set word limits and display word count
|
||||
getValueLength?: (value?: InputProps['value'] | string) => number;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export const ListTab: React.FC<PropsWithChildren<BotListHeaderProps>> = ({
|
||||
{...props}
|
||||
tabPaneMotion={false}
|
||||
type="button"
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- react 组件
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention -- react component
|
||||
renderTabBar={(innerProps, Node) => (
|
||||
<div className={cs(s.header, containerClass)}>
|
||||
<Node {...innerProps} />
|
||||
|
||||
@@ -22,7 +22,7 @@ import { type ButtonProps } from '@coze-arch/bot-semi/Button';
|
||||
import { UIButton, Toast, Spin } from '@coze-arch/bot-semi';
|
||||
|
||||
export type LoadingButtonProps = ButtonProps & {
|
||||
/** 加载中的 toast 文案 */
|
||||
/** Loading toast copy */
|
||||
loadingToast?: string | Omit<ToastReactProps, 'type'>;
|
||||
};
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ export const ActionBar: React.FC<ActionBarProps> = ({
|
||||
</span>
|
||||
</InsertLinkPopover>
|
||||
|
||||
{/* 暂时禁用,后续放开 */}
|
||||
{/* Temporarily disabled, later released */}
|
||||
{SHOW_ADD_NAME_BTN && (
|
||||
<Tooltip content={I18n.t('add_nickname')}>
|
||||
<UIButton
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface InsertLinkPopoverProps
|
||||
}
|
||||
|
||||
/**
|
||||
* 全受控
|
||||
* fully controlled
|
||||
*/
|
||||
export const InsertLinkPopover: React.FC<
|
||||
PropsWithChildren<InsertLinkPopoverProps>
|
||||
|
||||
@@ -29,7 +29,7 @@ export const getSyncInsertText = (action: SyncAction): string => {
|
||||
}
|
||||
|
||||
/**
|
||||
* 不应该走到这里
|
||||
* Shouldn't have come here
|
||||
*/
|
||||
primitiveExhaustiveCheck(type);
|
||||
return '';
|
||||
|
||||
@@ -78,7 +78,7 @@ export const useMarkdownEditor = ({
|
||||
},
|
||||
);
|
||||
|
||||
// 判断使用内置上传方法 or 自定义
|
||||
// Determine whether to use the built-in upload method or customize
|
||||
const selectUploadMethod = () => {
|
||||
if (customUpload) {
|
||||
return customUpload({
|
||||
@@ -143,7 +143,7 @@ export const useMarkdownEditor = ({
|
||||
ref.current.focus();
|
||||
const { selectionEnd } = ref.current;
|
||||
/**
|
||||
* 选中文字时点击 action bar, 将内容插入到文字的末尾
|
||||
* When the text is selected, click the action bar to insert the content at the end of the text
|
||||
*/
|
||||
console.log('handleInsertText', { value, insertText, selectionEnd });
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import { type UploadState } from '../type';
|
||||
import { UploadController } from '../service/upload-controller';
|
||||
|
||||
/**
|
||||
* 暂时没有场景,所以这里将多实例、一次行上传多文件的能力屏蔽了
|
||||
* There is no scene for the time being, so the ability to upload multiple files with multiple instances and one line is blocked here.
|
||||
*/
|
||||
export const useUpload = ({
|
||||
getUserId,
|
||||
|
||||
@@ -37,7 +37,7 @@ export interface MarkdownEditorProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* 全受控组件
|
||||
* fully controlled component
|
||||
*/
|
||||
export const MarkdownEditor: React.FC<MarkdownEditorProps> = ({
|
||||
value = '',
|
||||
|
||||
@@ -50,13 +50,13 @@ export interface UploadState {
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
// 自定义上传方法入参
|
||||
// Custom upload method imported parameters
|
||||
export interface CustomUploadParams {
|
||||
onUploadAllSuccess: (param: { url: string; fileName: string }) => void;
|
||||
}
|
||||
// 自定义上传方法出参
|
||||
// Custom upload method exported parameter
|
||||
export interface CustomUploadRes {
|
||||
uploadFileList: (fileList: File[]) => void;
|
||||
//null表示已完成
|
||||
//Null means completed
|
||||
uploadState: UploadState | null;
|
||||
}
|
||||
|
||||
@@ -31,11 +31,11 @@ export const matchAllTemplateRanges = (
|
||||
text: string,
|
||||
template: string,
|
||||
): { start: number; end: number }[] => {
|
||||
// 正则表达式,用于匹配双花括号内的内容
|
||||
// Regular expressions to match the contents of double curly braces
|
||||
const templateRegex = new RegExp(getFixedVariableTemplate(template), 'g');
|
||||
const matches: { start: number; end: number }[] = [];
|
||||
|
||||
// 循环查找所有匹配项
|
||||
// Loop through all matches
|
||||
while (true) {
|
||||
const match = templateRegex.exec(text);
|
||||
|
||||
|
||||
@@ -53,12 +53,12 @@ export interface OptionItemProps {
|
||||
avatar: string | undefined;
|
||||
name: string | undefined;
|
||||
searchWords?: string[];
|
||||
endPointName?: string; // 接入点名称(专业版有)
|
||||
endPointName?: string; // Access point name (available in the professional version)
|
||||
showEndPointName?: boolean;
|
||||
className?: string;
|
||||
/**
|
||||
* @deprecated
|
||||
* 原先只会有「限额」标签,M-5395720900 后会有大量新标签,避免兼容问题产品同意先简单隐藏掉标签展示
|
||||
* Originally, there would only be "limit" labels. After M-5395720900, there will be a large number of new labels to avoid compatibility problems. The product agrees to simply hide the label display first.
|
||||
*/
|
||||
tags?: OptionItemTag[];
|
||||
}
|
||||
@@ -77,12 +77,12 @@ export const ModelOptionItem: React.FC<OptionItemProps> = ({
|
||||
const tags: OptionItemTag[] = [];
|
||||
|
||||
const shouldShowEndPoint = showEndPointName && endPointName;
|
||||
// 即将支持,敬请期待
|
||||
// Support soon, so stay tuned.
|
||||
const displayName = FLAGS['bot.studio.model_select_switch_end_point_name_pos']
|
||||
? endPointName || name
|
||||
: name;
|
||||
|
||||
// 即将支持,敬请期待
|
||||
// Support soon, so stay tuned.
|
||||
const displayEndPointName = FLAGS[
|
||||
'bot.studio.model_select_switch_end_point_name_pos'
|
||||
]
|
||||
|
||||
@@ -23,11 +23,11 @@ import { MonetizeDescription } from '../monetize-description';
|
||||
import { MonetizeCreditRefreshCycle } from '../monetize-credit-refresh-cycle';
|
||||
|
||||
export interface MonetizeConfigValue {
|
||||
/** 是否开启付费 */
|
||||
/** Whether to start payment */
|
||||
isOn: boolean;
|
||||
/** 开启付费后,用户免费体验的次数 */
|
||||
/** The number of free user experiences after starting payment */
|
||||
freeCount: number;
|
||||
/** 刷新周期 */
|
||||
/** refresh cycle */
|
||||
refreshCycle: BotMonetizationRefreshPeriod;
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ interface MonetizeConfigPanelProps {
|
||||
value: MonetizeConfigValue;
|
||||
onChange: (value: MonetizeConfigValue) => void;
|
||||
/**
|
||||
* 内置防抖后的 onChange 事件,业务侧可选择性使用,正常只传 onChange 即可
|
||||
* (由于该组件是完全受控组件,因此不能只传 onDebouncedChange,必须传 onChange 实时更新视图)
|
||||
* The onChange event after built-in anti-shake can be selectively used by the business side. Normally, only onChange can be transmitted.
|
||||
* (Since this component is a fully controlled component, you cannot just pass onDebouncedChange, you must pass onChange to update the view in real time)
|
||||
*/
|
||||
onDebouncedChange?: (value: MonetizeConfigValue) => void;
|
||||
}
|
||||
|
||||
@@ -32,23 +32,23 @@ import s from './index.module.less';
|
||||
|
||||
interface MenuItem {
|
||||
/**
|
||||
* 如果是string,需传入starling key,并且会由div包一层
|
||||
* 如果是function,则自定义label的实现,active表示是否是选中态
|
||||
* If it is a string, you need to pass in the starling key, and it will be wrapped by the div layer.
|
||||
* If it is a function, the implementation of the custom label, active indicates whether it is selected
|
||||
*/
|
||||
label: string | ((active: boolean) => React.ReactNode);
|
||||
/** label 外的 badge,未来再扩展配置项 */
|
||||
/** The badge outside the label, and the configuration item will be expanded in the future */
|
||||
badge?: string;
|
||||
app: SpaceAppEnum;
|
||||
/**
|
||||
* Q:为什么不叫 visible?FG 要取反,filter() 也要取反,很麻烦
|
||||
* A:为了兼容旧配置,缺省时认定为 visible。避免合码时无冲突 导致忽略掉新增配置的问题。
|
||||
* Q: Why is it not called visible? FG should be reversed, and filter () should also be reversed, which is very troublesome.
|
||||
* A: In order to be compatible with the old configuration, it is recognized as visible by default. To avoid no conflicts when combining codes, the problem of new configurations is ignored.
|
||||
*/
|
||||
invisible?: boolean;
|
||||
/** 目前(24.05.21)没发现用处,怀疑是以前的功能迭代掉了,@huangjian 说先留着 */
|
||||
/** At present (24.05.21) no use is found, it is suspected that the previous function iteration is lost, @huangjian said to keep it first */
|
||||
icon?: ReactNode;
|
||||
/** 目前(24.05.21)没发现用处,怀疑是以前的功能迭代掉了,@huangjian 说先留着 */
|
||||
/** At present (24.05.21) no use is found, it is suspected that the previous function iteration is lost, @huangjian said to keep it first */
|
||||
selectedIcon?: ReactNode;
|
||||
/** 自动化打标 */
|
||||
/** Automatic marking */
|
||||
e2e?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import React, {
|
||||
import classNames from 'classnames';
|
||||
|
||||
import s from './index.module.less';
|
||||
// react-markdown 20ms 左右的 longtask
|
||||
// React-markdown longtask around 20ms
|
||||
const LazyReactMarkdown = lazy(() => import('react-markdown'));
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const ReactMarkdown = (props: any) => (
|
||||
|
||||
@@ -94,10 +94,10 @@ export type ProjectTemplateCopySuccessCallback = (param: {
|
||||
|
||||
export const useProjectTemplateCopyModal = (props: {
|
||||
modalTitle: string;
|
||||
/** 是否需要选择 space */
|
||||
/** Do you need to choose space? */
|
||||
isSelectSpace: boolean;
|
||||
onSuccess?: ProjectTemplateCopySuccessCallback;
|
||||
/** 埋点参数 - 当前页面/来源 */
|
||||
/** Event tracking parameters - current page/source */
|
||||
source: NonNullable<
|
||||
ParamsTypeDefine[EVENT_NAMES.template_action_front]['source']
|
||||
>;
|
||||
@@ -167,7 +167,7 @@ export const useProjectTemplateCopyModal = (props: {
|
||||
formProps={{
|
||||
initValues,
|
||||
onValueChange: val => {
|
||||
// 当用户删除 input 中所有字符时,val.name 字段会消失,而不是空字符串,神秘
|
||||
// When the user removes all characters in input, val.name field disappears instead of empty string
|
||||
setIsFormValid(!!val.name?.trim());
|
||||
},
|
||||
getFormApi: api => {
|
||||
@@ -181,7 +181,7 @@ export const useProjectTemplateCopyModal = (props: {
|
||||
sourceProduct: inputSourceProduct,
|
||||
}: {
|
||||
initValue: ProjectTemplateCopyValue;
|
||||
/** 用于提取埋点参数 */
|
||||
/** Used to extract event tracking parameters */
|
||||
sourceProduct: ProductInfo;
|
||||
}) => {
|
||||
setInitValues({
|
||||
|
||||
@@ -26,7 +26,7 @@ import classnames from 'classnames';
|
||||
|
||||
import s from './handle.module.less';
|
||||
|
||||
// 目前只支持水平方向,按需扩展吧
|
||||
// Currently only supports horizontal direction, expand it on demand.
|
||||
export interface ResizableLayoutHandleProps {
|
||||
className?: string;
|
||||
hotZoneClassName?: string;
|
||||
@@ -94,7 +94,7 @@ export const ResizableLayoutHandle: FC<ResizableLayoutHandleProps> = ({
|
||||
|
||||
const offEvents = () => {
|
||||
window.removeEventListener('pointermove', move, false);
|
||||
// 适配移动端出现多点触控的情况
|
||||
// Adapt to the situation of multi-touch on the mobile end
|
||||
window.removeEventListener('pointerdown', moveEnd, false);
|
||||
window.removeEventListener('pointerup', moveEnd, false);
|
||||
window.removeEventListener('pointercancel', moveEnd, false);
|
||||
@@ -108,12 +108,12 @@ export const ResizableLayoutHandle: FC<ResizableLayoutHandleProps> = ({
|
||||
setMoving(true);
|
||||
callbackRef.current.onMoveStart();
|
||||
window.addEventListener('pointermove', move, false);
|
||||
// 适配移动端出现多点触控的情况
|
||||
// Adapt to the situation of multi-touch on the mobile end
|
||||
window.addEventListener('pointerdown', moveEnd, false);
|
||||
window.addEventListener('pointerup', moveEnd, false);
|
||||
window.addEventListener('pointercancel', moveEnd, false);
|
||||
};
|
||||
// TODO hover 样式 & 热区宽度需要和 UI 对齐
|
||||
// TODO hover style & hotzone width needs to be aligned with UI
|
||||
return (
|
||||
<div
|
||||
className={classnames(hotZoneStyle, hotZoneClassName)}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import {
|
||||
Children,
|
||||
type PropsWithChildren,
|
||||
@@ -61,7 +61,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
|
||||
return;
|
||||
}
|
||||
const totalSize = sum(state.itemWidth);
|
||||
// 排除还没有进行过拖拽的情况,此时本地 state 中没有记录上次分配的宽度
|
||||
// Exclude the case where no drag has been performed, and the last allocated width is not recorded in the local state at this time
|
||||
if (totalSize <= 0) {
|
||||
return;
|
||||
}
|
||||
@@ -107,7 +107,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
|
||||
);
|
||||
}
|
||||
}
|
||||
// @ts-expect-error -- 跳过类型体操
|
||||
// @ts-expect-error -- skip type gymnastics
|
||||
const { ref } = child;
|
||||
if (typeof ref === 'function') {
|
||||
ref(target);
|
||||
@@ -144,7 +144,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
|
||||
),
|
||||
});
|
||||
}}
|
||||
// 相对于初始位置的偏移量
|
||||
// Offset from the initial position
|
||||
onMove={offset => {
|
||||
const pre = index - 1;
|
||||
childRef.current[pre].style.width = `${
|
||||
@@ -156,7 +156,7 @@ export const ResizableLayout: FC<PropsWithChildren<ResizableLayoutProps>> = ({
|
||||
}}
|
||||
onMoveEnd={() => {
|
||||
setState({
|
||||
// 拖拽结束后,记录真实宽度
|
||||
// After dragging, record the true width
|
||||
itemWidth: childRef.current.map(
|
||||
item => item.clientWidth ?? 0,
|
||||
),
|
||||
|
||||
@@ -24,7 +24,7 @@ import styles from './index.module.less';
|
||||
|
||||
export interface SearchProps {
|
||||
defaultValue?: string;
|
||||
/** 当此值变化时,更新内部的搜索内容 */
|
||||
/** When this value changes, update the internal search content */
|
||||
refreshValue?: string;
|
||||
onSearch?: (value?: string) => void;
|
||||
placeholder?: string;
|
||||
|
||||
@@ -106,7 +106,7 @@ export const SelectSpaceModal: React.FC<
|
||||
const params = form.current?.formApi.getValues();
|
||||
await onConfirm?.(params?.spaceId ?? '', params?.name);
|
||||
} catch {
|
||||
// 审核不通过会走到这个逻辑,调用接口处会上报自定义异常
|
||||
// If the review fails, you will go to this logic, and a custom exception will be reported at the calling interface.
|
||||
setLoading(false);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -21,7 +21,7 @@ export type OnMove<TId = string | number> = (
|
||||
souceId: TId,
|
||||
targetId: TId,
|
||||
isBefore: boolean,
|
||||
) => void; // 因为没有顺序信息,所以需要指明是在前面还是后面
|
||||
) => void; // Since there is no order information, it is necessary to specify whether it is before or after
|
||||
export interface UseDndSortableParams<TId = string | number> {
|
||||
id: TId;
|
||||
type: symbol;
|
||||
@@ -61,7 +61,7 @@ export const useDnDSortableItem = <TId = string | number>({
|
||||
if (!itemDomRef.current || item.id === id) {
|
||||
return;
|
||||
}
|
||||
// 当前被拖拽元素的 X Y 坐标
|
||||
// The X Y coordinates of the currently dragged element
|
||||
const draggingClient = monitor.getClientOffset();
|
||||
const dropTargetClient = itemDomRef.current?.getBoundingClientRect();
|
||||
let isBefore = false;
|
||||
|
||||
@@ -87,7 +87,7 @@ export const SortableList = <TData extends object>({
|
||||
enabled={enabled}
|
||||
direction={direction}
|
||||
/**
|
||||
* 原始数组 [1, 2, target, 4, 5, source, 7, 8]
|
||||
* Raw array [1, 2, target, 4, 5, source, 7, 8]
|
||||
* before = true j==> [1,2, source, target, 4, 5, 7, 8]
|
||||
* before = false ==> [1,2, target, source, 4, 5, 7, 8]
|
||||
**/
|
||||
@@ -101,7 +101,7 @@ export const SortableList = <TData extends object>({
|
||||
newList.findIndex(target => getId(target) === targetId) +
|
||||
(before ? 0 : 1);
|
||||
if (sourceIndex === targetIndex) {
|
||||
// 前后 index 相同的情况不触发 onChange 避免频繁 rerender
|
||||
// Do not trigger onChange if the index is the same before and after to avoid frequent rerender
|
||||
return;
|
||||
}
|
||||
sourceItem && newList.splice(targetIndex, 0, sourceItem);
|
||||
|
||||
@@ -58,7 +58,7 @@ export const BaseSpaceFormSelect = withField(
|
||||
return;
|
||||
}
|
||||
|
||||
// 需要触发表单 onChange 事件 否则上层响应不到数据变化的事件
|
||||
// The form onChange event needs to be triggered, otherwise the upper layer will not respond to the data change event.
|
||||
props.onChange?.(fixedInitValue);
|
||||
}, [fixedInitValue]);
|
||||
|
||||
|
||||
@@ -20,24 +20,24 @@ import cls from 'classnames';
|
||||
import { useScroll } from 'ahooks';
|
||||
|
||||
interface StickyProps {
|
||||
/** 滚动容器,也就是那个 scrollHeight 大于 viewportHeight,overflow-y auto/scroll 的容器 */
|
||||
/** Scroll container, that is, the container with scrollHeight greater than viewportHeight and overflow-y auto/scroll */
|
||||
scrollContainerRef: () => Element;
|
||||
/**
|
||||
* 作用同 css sticky 时的 top 属性
|
||||
* The top property when used as a css sticky
|
||||
* @default 0
|
||||
*/
|
||||
top?: number;
|
||||
/**
|
||||
* 触发 sticky 后,底部(为了美观)额外的滚动距离
|
||||
* After triggering sticky, the bottom (for aesthetics) has an extra scroll distance
|
||||
* @default 0
|
||||
*/
|
||||
bottom?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* sticky 容器组件,用于解决 sticky 元素高于视窗时无法全部露出的问题
|
||||
* Sticky container component, used to solve the problem that sticky elements cannot be fully exposed when they are higher than the viewport
|
||||
*
|
||||
* 效果是触发 sticky 后,sticky 容器会跟随滚动容器的滚动而有限地上下移动
|
||||
* The effect is that after triggering the sticky, the sticky container will follow the scroll of the scrolling container and move up and down limited.
|
||||
*/
|
||||
export function Sticky({
|
||||
top: stickyTop = 0,
|
||||
@@ -46,11 +46,11 @@ export function Sticky({
|
||||
children,
|
||||
}: PropsWithChildren<StickyProps>) {
|
||||
const stickyContainerRef = useRef<HTMLDivElement>(null);
|
||||
/** 一个不可见的元素,用于通过 IntersectionObserver 检测是否已经 sticky */
|
||||
/** An invisible element used to detect whether it is sticky by IntersectionObserver */
|
||||
const stickyDetectRef = useRef<HTMLDivElement>(null);
|
||||
const [isSticky, setIsSticky] = useState(false);
|
||||
const prevScrollTop = useRef(scrollContainerRef()?.scrollTop || 0);
|
||||
// sticky 容器模拟向上滚动的距离
|
||||
// Sticky container simulates the distance of scrolling up
|
||||
const [simulateScrollDistance, setSimulateScrollDistance] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -58,7 +58,7 @@ export function Sticky({
|
||||
return;
|
||||
}
|
||||
|
||||
/** IntersectionObserver 监听是否触发 sticky */
|
||||
/** IntersectionObserver monitor if sticky is triggered */
|
||||
const intersectionObserver = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
const { isIntersecting } = entry;
|
||||
@@ -73,34 +73,34 @@ export function Sticky({
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 已测试该方法能监听 `scrollTo` 等方法产生的 scroll,无论模式是 smooth 还是 instant
|
||||
// It has been tested that this method can listen to scrolls generated by methods such as'scrollTo ', regardless of whether the mode is smooth or instant.
|
||||
useScroll(scrollContainerRef, scrollEvent => {
|
||||
/** 页面整体向上(scrollbar 向下移动)滚动的距离,为负则代表页面向下滚动 */
|
||||
/** The distance where the page scrolls up as a whole (scrollbar moves down). If it is negative, it means the page scrolls down. */
|
||||
const scrollUpDistance = scrollEvent.top - prevScrollTop.current;
|
||||
prevScrollTop.current = scrollEvent.top;
|
||||
|
||||
if (!stickyContainerRef.current || !isSticky) {
|
||||
// return false 避免 useScroll 产生 rerender,下同
|
||||
// (回调内的其他 setState 会正常触发 rerender)
|
||||
// Return false to avoid useScroll rerender
|
||||
// (Other setStates within the callback will trigger rerender normally)
|
||||
return false;
|
||||
}
|
||||
|
||||
const viewportHeight = window.innerHeight;
|
||||
const stickyContainerHeight = stickyContainerRef.current?.scrollHeight || 0;
|
||||
|
||||
/** 触发 sticky 后,模拟滚动的容器高度 */
|
||||
/** After triggering sticky, simulate the height of the rolling container */
|
||||
const simulateStickyContainerHeight =
|
||||
stickyContainerHeight + stickyTop + stickyBottom;
|
||||
// 判断高度是否小于 viewport,是的话始终能正常显示在视图内,不用后面乱七八糟一堆计算了
|
||||
// Determine whether the height is less than the viewport. If so, it can always be displayed in the view normally, and there is no need to make a mess of calculations later.
|
||||
if (simulateStickyContainerHeight < viewportHeight) {
|
||||
return false;
|
||||
}
|
||||
/** 模拟滚动容器比视窗高出的部分,也即模拟滚动的上限 */
|
||||
/** The part of the simulated scrolling container higher than the viewport, that is, the upper limit of the simulated scrolling */
|
||||
const simulateMaxScrollDistance =
|
||||
simulateStickyContainerHeight - viewportHeight;
|
||||
|
||||
if (scrollUpDistance > 0) {
|
||||
// #region 处理向上滚动
|
||||
// #region processing scroll up
|
||||
const stickyReachedBottom =
|
||||
simulateScrollDistance >= simulateMaxScrollDistance;
|
||||
if (stickyReachedBottom) {
|
||||
@@ -116,7 +116,7 @@ export function Sticky({
|
||||
return false;
|
||||
// #endregion
|
||||
} else {
|
||||
// #region 处理向下滚动
|
||||
// #region processing scroll down
|
||||
const stickyReachedTop = simulateScrollDistance <= 0;
|
||||
if (stickyReachedTop) {
|
||||
setSimulateScrollDistance(0);
|
||||
|
||||
@@ -56,7 +56,7 @@ export const TableSelectAllPopover: FC<
|
||||
>
|
||||
{I18n.t('publish_permission_control_page_remove_choose_all')}
|
||||
</Checkbox>
|
||||
{/* 确保全选和右侧区域有一个最小间隔 */}
|
||||
{/* Make sure there is a minimum interval between Select All and the right area */}
|
||||
<div className="flex-1 min-w-[40px]" />
|
||||
{renderCount ? (
|
||||
<div>
|
||||
|
||||
@@ -28,12 +28,12 @@ import {
|
||||
type ParamsTypeDefine,
|
||||
} from '@coze-arch/bot-tea';
|
||||
|
||||
/** 后续考虑通用化封装,增加诸如延迟时间、露出比例等参数 */
|
||||
/** Subsequent consideration of generalized packaging, adding parameters such as delay time and exposure ratio */
|
||||
type TeaExposureProps<TEventName extends EVENT_NAMES> = {
|
||||
/**
|
||||
* 是否只上报一次
|
||||
* Is it only reported once?
|
||||
* @default false
|
||||
* @todo 有余力再考虑兼容虚拟滚动
|
||||
* @Todo has the spare time to reconsider compatibility with virtual scrolling
|
||||
*/
|
||||
once?: boolean;
|
||||
teaEvent: {
|
||||
@@ -44,13 +44,13 @@ type TeaExposureProps<TEventName extends EVENT_NAMES> = {
|
||||
HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
/**
|
||||
* 曝光埋点上报组件
|
||||
* 可以当成普通的 div 来用(比如配置样式)
|
||||
* Exposure event tracking report component
|
||||
* It can be used as a normal div (e.g. configuration style).
|
||||
*
|
||||
* 封装的意义:避免组件 rerender
|
||||
* The meaning of encapsulation: avoid component rerendering
|
||||
*
|
||||
* useInViewport 会造成组件频繁 rerender,哪怕你并未使用其返回值,只要调用了这个 hook,就会随着其内部的 setState 触发 rerender。
|
||||
* 而我们都知道,对于下面这段代码,A rerender 会触发 B 和 C 的rerender;而 B rerender 不会触发 A 和 C 的 rerender。
|
||||
* useInViewport causes the component to rerender frequently, even if you don't use its return value. As long as this hook is called, rerender will be triggered with its internal setState.
|
||||
* And we all know that for the following code, A rerender will trigger the rerender of B and C, while B rerender will not trigger the rerender of A and C.
|
||||
* ```
|
||||
* const A = () => <B><C /></B>
|
||||
* ```
|
||||
|
||||
@@ -23,7 +23,7 @@ vi.stubGlobal('SAMI_APP_KEY', vi.fn());
|
||||
vi.stubGlobal('IS_DEV_MODE', false);
|
||||
vi.stubGlobal('IS_OVERSEA', false);
|
||||
|
||||
// Mock Canvas API 完整版本
|
||||
// Mock Canvas API full version
|
||||
const createMockCanvas = () => ({
|
||||
getContext: vi.fn(() => ({
|
||||
fillRect: vi.fn(),
|
||||
@@ -82,14 +82,14 @@ if (global.document) {
|
||||
});
|
||||
}
|
||||
|
||||
// CSS 文件 mock
|
||||
// CSS file mock
|
||||
vi.mock('*.css', () => ({}));
|
||||
vi.mock('*.scss', () => ({}));
|
||||
vi.mock('*.sass', () => ({}));
|
||||
vi.mock('*.less', () => ({}));
|
||||
vi.mock('*.styl', () => ({}));
|
||||
|
||||
// Mock lottie-web 完整版本
|
||||
// Mock lottie-web full version
|
||||
vi.mock('lottie-web', () => ({
|
||||
default: {
|
||||
loadAnimation: vi.fn(() => ({
|
||||
|
||||
Reference in New Issue
Block a user