chore: replace all cn comments of fe to en version by volc api (#320)

This commit is contained in:
tecvan
2025-07-31 10:32:15 +08:00
committed by GitHub
parent 716ec0cba8
commit 71f6245a01
2960 changed files with 15545 additions and 15545 deletions

View File

@@ -14,6 +14,6 @@
background-color: #AAA;
border-radius: 8px;
transition: height 0.1s ease-in-out; /* 设置过渡效果 */
transition: height 0.1s ease-in-out; /* Set transition effects */
}
}

View File

@@ -34,7 +34,7 @@ const MoreButton: FC<IProps> = props => {
return (
<UIKitTooltip
// 为了点调起选择文件的事件时收起 tooltip
// Collapse tooltip when the selected file event is invoked
disableFocusListener
content={tooltipContent}
hideToolTip={layout === Layout.MOBILE}

View File

@@ -181,15 +181,15 @@ export const ChatInput = forwardRef<InputRefObject, IChatInputProps>(
onClearContext?.();
};
/**
* 处理清除历史记录按钮点击事件
* Handle the Clear History button click event
*/
const handleClearHistoryButtonClick = () => {
onClearHistory?.();
};
/**
* 处理用户发送消息
* @param text 用户发送消息的文本
* Handle user sending messages
* @Param text The text of the message sent by the user
*/
const handleSendMessage = (text: string) => {
onSendMessage?.({
@@ -223,7 +223,7 @@ export const ChatInput = forwardRef<InputRefObject, IChatInputProps>(
if (
!isMultiLines &&
height > DEFAULT_HEIGHT &&
// 通过 inputText 长度判断,排除 placeholder 导致的 resize 不处理
// Judging by the length of inputText, excluding the resize caused by placeholder will not be processed.
inputText?.trim()?.length !== 0
) {
setIsMultiLines(true);
@@ -238,7 +238,7 @@ export const ChatInput = forwardRef<InputRefObject, IChatInputProps>(
}
};
/** 计算出复合条件的readonly值 */
/** Calculate the readonly value of the compound condition */
const finalClearHistoryButtonDisable =
isClearHistoryButtonDisabled || isReadonly;
const finalSendButtonDisable =

View File

@@ -45,7 +45,7 @@ export const useTextSend = ({
onSubmit: (text: string) => void;
defaultValue?: string;
/**
* 是否允许空字符串提交
* Whether to allow empty string submission
* @default false
*/
allowEmpty?: boolean;
@@ -172,10 +172,10 @@ export const useTextSend = ({
return;
}
// 计算光标的当前位置
// Calculate the current position of the cursor
const cursorPosition = textarea.selectionStart;
// 在光标位置插入新行
// Inserts a new row at the cursor position
const newValue = `${inputText.substring(
0,
cursorPosition,

View File

@@ -29,18 +29,18 @@ import {
interface IChatUploadProps {
/**
* 上传事件回调
* @param uploadType 上传类型 [IMAGE=0 FILE=1]
* @param file 文件
* Upload event callback
* @param uploadType [IMAGE = 0 FILE = 1]
* @param file
* @returns void
*/
onUpload: (uploadType: UploadType, file: File) => void;
/**
* 文案信息配置
* Copywriting information configuration
*/
copywritingConfig?: IChatUploadCopywritingConfig;
/**
* 文件最大尺寸(单位byte
* Maximum file size (in bytes)
*/
maxFileSize?: number;
isDisabled?: boolean;
@@ -66,8 +66,8 @@ export const ChatUpload: FC<IChatUploadProps> = props => {
} = props;
/**
* 处理上传
* @param fileList 文件List
* handle uploads
* @param fileList
* @returns void
*/
const handleUpload = (fileList: File[]) => {
@@ -86,13 +86,13 @@ export const ChatUpload: FC<IChatUploadProps> = props => {
return;
}
// 是否存在超出大小的文件
// Is there a file that is out of size?
const hasOverflowLimitFileSize = fileList.some(
file => file.size > maxFileSize,
);
const hasEmptyFile = fileList.some(file => file.size <= 0);
// 文件大小超过预期大小的错误处理
// Error handling if the file size exceeds the expected size
if (hasOverflowLimitFileSize) {
Toast.warning({
showClose: false,

View File

@@ -57,13 +57,13 @@ interface OnBoardingProps {
prologue?: string;
suggestionList?: IMessage[];
/**
* suggestionList是否换行展示, 默认false
* SuggestionList Whether to wrap display, default false
*/
suggestionsWrap?: boolean;
readonly?: boolean;
suggestionListWithString?: string[];
/**
* suggestionListWithString是否换行展示, 默认false
* suggestionListWithString whether to wrap, default false
*/
suggestionsWithStringWrap?: boolean;
onSuggestionClick?: (content: string) => void;

View File

@@ -45,7 +45,7 @@ export const Gradient: React.FC<{
'transition-all duration-500': !preview,
})}
style={{
[direction]: `${(position > 0 ? position : 0) * 100 - 0.1}%`, // 0.1为阴影补偿,防止出现间隙
[direction]: `${(position > 0 ? position : 0) * 100 - 0.1}%`, // 0.1 is shadow compensation to prevent gaps.
width: '10%',
background,
opacity: showGradient ? 1 : 0,
@@ -58,7 +58,7 @@ const getGradient = (
cropperWidth = 1,
) => {
const { left: cropperImgLeft = 0, width: cropperImgWidth = 0 } = canvasData;
// 伪裁剪 兼容历史gradient不准的问题
// Pseudo-cropping, compatibility with inaccurate historical gradients
if (!isEmpty(canvasData)) {
return {
left: cropperImgLeft / cropperWidth,
@@ -97,7 +97,7 @@ export const WithRuleImgBackground: React.FC<WithRuleImgBackgroundProps> = ({
const [themeColor, setThemeColor] = useState(theme_color ?? 'transparent');
// 与裁剪框等比例算出图片渲染区域的宽度
// Calculate the width of the image rendering area in proportion to the cropping box
const imgWidth = (targetHeight * cropperSize.width) / cropperSize.height;
const mediumColor = addAlpha(themeColor, 0.95);
@@ -120,7 +120,7 @@ export const WithRuleImgBackground: React.FC<WithRuleImgBackgroundProps> = ({
zIndex: preview ? 100 : 0,
}}
>
{/* 背景图上压黑阴影 */}
{/* Black shadow on background cover */}
<div className="bg-[rgba(0,0,0,0.12)] absolute w-full h-full z-[200] rounded-t-[6px]"></div>
<div className="relative w-fit h-fit left-1/2 -translate-x-1/2">

View File

@@ -27,22 +27,22 @@ export interface CanvasPosition {
}
export interface BackgroundImageDetail {
/** 原始图片 */
/** original image */
origin_image_uri?: string;
origin_image_url?: string;
/** 实际使用图片 */
/** Actual use of pictures */
image_uri?: string;
image_url?: string;
theme_color?: string;
/** 渐变位置 */
/** Gradual change of position */
gradient_position?: GradientPosition;
/** 裁剪画布位置 */
/** Crop canvas position */
canvas_position?: CanvasPosition;
}
export interface BackgroundImageInfo {
/** web端背景图 */
/** Web background cover */
web_background_image?: BackgroundImageDetail;
/** 移动端背景图 */
/** Mobile end background cover */
mobile_background_image?: BackgroundImageDetail;
}

View File

@@ -16,7 +16,7 @@
import { MODE_CONFIG } from './const';
// 输入透明度系数 和color 返回新的颜色
// Enter the transparency factor and color to return a new color
export function addAlpha(color: string, alpha: number): string {
const regex = /^rgba\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/;
if (!regex.test(color)) {
@@ -31,11 +31,11 @@ export function addAlpha(color: string, alpha: number): string {
return newColor;
}
// 图片的宽高比
// Image aspect ratio
export const getStandardRatio = (mode: 'pc' | 'mobile'): number =>
MODE_CONFIG[mode].size.width / MODE_CONFIG[mode].size.height;
// 计算是否展示渐变阴影 = 屏幕宽度 > 图片宽度 * 1- 2 * 左/右阴影位置)
// Calculate whether to display gradual change Shadow = Screen Width > Image Width * (1- 2 * Left/Right Shadow Position)
export const computeShowGradient = (
width: number,
imgWidth: number,

View File

@@ -60,39 +60,39 @@ export interface EnhancedContentConfig {
export interface IContentBoxProps {
/**
* Core SDK的消息体内容
* Message body content of the Core SDK
*/
message: IMessage;
/**
* 事件回调对象
* event callback object
*/
eventCallbacks?: IEventCallbacks;
/**
* content卡片配置的内容
* Content of the card configuration
*/
contentConfigs?: IContentConfigs;
/**
* 是否只读
* Is it read-only?
*/
readonly?: boolean;
getBotInfo: GetBotInfo;
layout: Layout;
/**
* mix 模式下,给 text 格式卡片加插槽
* In mix mode, slot the text card
*/
multimodalTextContentAddonTop?: ReactNode;
showBackground: boolean;
/**
* 启用自动适应图片能力
* Enable the ability to automatically adapt pictures
*/
enableAutoSizeImage?: boolean;
/**
* mdBox的配置
* mdBox configuration
*/
mdBoxProps?: MdBoxProps;
/**
* 卡片状态是否为disabled
* Is the card status disabled?
*/
isCardDisabled?: boolean;
isContentLoading?: boolean;
@@ -114,7 +114,7 @@ export const ContentBox: FC<IContentBoxProps> = props => {
enhancedContentConfigList,
} = props;
/**
* Content内容启用配置 Start
* Content Enable Configuration Start
*/
const isTextEnable = defaultEnable(
contentConfigs?.[ContentBoxType.TEXT]?.enable,
@@ -127,7 +127,7 @@ export const ContentBox: FC<IContentBoxProps> = props => {
const isSimpleFunctionEnable =
contentConfigs?.[ContentBoxType.SIMPLE_FUNCTION]?.enable;
/**
* Content内容启用配置 End
* Content Enable Configuration End
*/
const enhancedContentConfig = enhancedContentConfigList?.find(config =>
@@ -143,8 +143,8 @@ export const ContentBox: FC<IContentBoxProps> = props => {
});
}
/**
* 文本类型的处理
* 这里目前有两种情况,第一种message.type = 'follow_up' 代表是suggestion 第二种反之是普通文本消息
* Handling of text types
* There are currently two cases here, the first is message.type = 'follow_up' means suggestion, the second is plain text message
*/
if (message.content_type === ContentType.Text) {
const { eventCallbacks, mdBoxProps } = props;
@@ -174,7 +174,7 @@ export const ContentBox: FC<IContentBoxProps> = props => {
}
/**
* FIle类型的内容
* FIle type content
*/
if (message.content_type === ContentType.File && isFileEnable) {
const { copywriting, fileAttributeKeys } =
@@ -198,7 +198,7 @@ export const ContentBox: FC<IContentBoxProps> = props => {
}
/**
* 图片类型的内容
* Image type content
*/
if (message.content_type === ContentType.Image && isImageEnable) {
const { eventCallbacks } = props;
@@ -224,7 +224,7 @@ export const ContentBox: FC<IContentBoxProps> = props => {
}
/**
* function类型
* Function type
*/
if (message.type === 'function_call' && isSimpleFunctionEnable) {
const { copywriting } =
@@ -236,7 +236,7 @@ export const ContentBox: FC<IContentBoxProps> = props => {
}
/**
* 文件文字同时发送的多模态消息
* Multimodal messages with simultaneous text transmission
*/
if (
message.content_type === ContentType.Mix &&

View File

@@ -20,7 +20,7 @@ import classNames from 'classnames';
import './index.less';
/**
* 套壳组件,默认宽度通栏,用于帮助孤立组件与 message box 保持宽度对齐
* Sleeve component, the default width bar, is used to help the isolated component maintain width alignment with the message box
*/
export const FullWidthAligner = (
props: PropsWithChildren<{

View File

@@ -133,7 +133,7 @@ export const MessageBoxWrap: FC<
const isHovering = useHover(() => wrapRef.current);
// 适配移动端 移动端没有hover效果使用点击交互
// Adapt mobile end mobile end has no hover effect, use click interaction
const [hoverContentVisible, setHoverContentVisible] =
useState<boolean>(false);
@@ -187,7 +187,7 @@ export const MessageBoxWrap: FC<
// chat-uikit-message-box-container__message
className="flex-1 max-w-[calc(100%-44px)]"
>
{/* TODO: 不支持一条消息内渲染多个 content */}
{/* TODO: Rendering multiple content within a message is not supported */}
<div
// chat-uikit-message-box-container__message__message-box
className="relative flex flex-col w-fit max-w-full"
@@ -293,7 +293,7 @@ export const MessageBoxWrap: FC<
{right}
</div>
</div>
{/* 对这个 dom 的样式改动前请先阅读上方的 refreshContainerWidthConditionally 方法 */}
{/* Please read the refreshContainerWidthConditionally above before changing the style of this dom */}
<div
ref={messageFooterRef}
// chat-uikit-message-box-container__message__message-box__footer

View File

@@ -27,30 +27,30 @@ import {
import { type UserLabelInfo } from '../user-label';
/**
* 样式主题
* @deprecated 考虑替换实现方式,目前使用不够灵活
* style theme
* @Deprecated Consider alternative implementations, currently not flexible enough
*/
export type MessageBoxTheme =
/** 主题色 */
/** theme color */
| 'primary'
/** 白色背景 */
/** White Background */
| 'whiteness'
/**
* 灰色背景
* Grey background
*/
| 'grey'
/** home用的炫彩底色 */
/** Colorful base colors for home use */
| 'colorful'
/** 官方通知,有彩色边框 */
/** Official notice with colored borders */
| 'color-border'
/** 官方通知,有彩色边框,但是不留 padding */
/** Official notice, with colored borders, but no padding. */
| 'color-border-card'
| 'border'
| 'none';
interface MessageBoxBasicProps {
/**
* 用户信息
* user information
*/
senderInfo: {
userUniqueName?: string;
@@ -60,77 +60,77 @@ interface MessageBoxBasicProps {
userLabel?: UserLabelInfo | null;
};
/**
* 消息 id
* Message ID
*/
messageId: string | null;
showUserInfo?: boolean;
/**
* 主题
* theme
*/
theme?: MessageBoxTheme;
/**
* 插入消息的footer
* Insert the footer of the message
*/
renderFooter?: (refreshContainerWidth: () => void) => React.ReactNode;
/** 鼠标悬浮时展示的组件 */
/** Components displayed while the mouse is hovering */
hoverContent?: ReactNode;
/**
* 左侧插槽
* Left Slot
*/
right?: React.ReactNode;
/**
* 右上角插槽
* Upper right slot
*/
topRightSlot?: React.ReactNode;
getBotInfo: GetBotInfo;
/**
* 是否是移动端
* Is it a mobile end?
*/
layout?: Layout;
classname?: string;
messageBubbleWrapperClassname?: string;
messageBoxWraperClassname?: string; // message box的直接父亲样式
messageBubbleClassname?: string; // message消息气泡的样式
messageErrorWrapperClassname?: string; // message错误的父亲样式
isHoverShowUserInfo?: boolean; // hover的时候是否显示用户详细信息
messageBoxWraperClassname?: string; // Direct father style of message box
messageBubbleClassname?: string; // Message The style of the message bubble
messageErrorWrapperClassname?: string; // Message wrong father style
isHoverShowUserInfo?: boolean; // Whether to display user details when hovering
showBackground?: boolean;
/**
* 容器动态宽度,用于动态计算图片尺寸
* Container dynamic width for dynamically calculating image dimensions
*/
imageAutoSizeContainerWidth?: number;
/**
* 是否启用图片自适应模式
* Whether to enable picture adaptation mode
*/
enableImageAutoSize?: boolean;
/**
* 事件回调
* event callback
*/
eventCallbacks?: IEventCallbacks;
/**
* 针对 JS Error 的响应
* Response to JS Error
*/
onError?: (error: unknown) => void;
}
/** 只是套壳,内容由 children 呈现 */
/** It's just a shell, the content is presented by children */
export interface MessageBoxShellProps extends MessageBoxBasicProps {
children: React.ReactNode;
}
/** 含有完整内置实现的 MessageBox */
/** MessageBox with full built-in implementation */
export interface NormalMessageBoxProps extends MessageBoxBasicProps {
/**
* 消息体
* message body
*/
message: IMessage;
/**
* 文件需要用到的必备参数
* Required parameters for the file
*/
contentConfigs?: IContentConfigs;
/** 样式主题 */
/** style theme */
theme?: MessageBoxTheme;
footer?: ReactNode;
readonly?: boolean;
@@ -147,11 +147,11 @@ export interface MessageBoxWrapProps {
showUserInfo?: boolean;
renderFooter?: (refreshContainerWidth: () => void) => React.ReactNode;
/** 鼠标悬浮时展示的组件 */
/** Components displayed while the mouse is hovering */
hoverContent?: React.ReactNode;
right?: React.ReactNode;
/**
* 右上角插槽
* Upper right slot
*/
topRightSlot?: React.ReactNode;
messageId: string | null;
@@ -160,11 +160,11 @@ export interface MessageBoxWrapProps {
contentTime: number | undefined;
classname?: string;
messageBoxWraperClassname?: string; // message box的直接父亲样式
messageBubbleClassname?: string; // message消息气泡的样式
messageBubbleWrapperClassname?: string; // message消息气泡的父亲样式
messageErrorWrapperClassname?: string; // message错误的父亲样式
isHoverShowUserInfo?: boolean; // hover的时候是否显示用户详细信息
messageBoxWraperClassname?: string; // Direct father style of message box
messageBubbleClassname?: string; // Message The style of the message bubble
messageBubbleWrapperClassname?: string; // Message message bubble father style
messageErrorWrapperClassname?: string; // Message wrong father style
isHoverShowUserInfo?: boolean; // Whether to display user details when hovering
showBackground?: boolean;
extendedUserInfo?: {
@@ -172,16 +172,16 @@ export interface MessageBoxWrapProps {
userUniqueName?: string;
};
/**
* 容器动态宽度,用于动态计算图片尺寸
* Container dynamic width for dynamically calculating image dimensions
*/
imageAutoSizeContainerWidth?: number;
/**
* 是否启用图片自适应模式
* Whether to enable picture adaptation mode
*/
enableImageAutoSize?: boolean;
eventCallbacks?: IEventCallbacks;
/**
* 针对 JS Error 的响应
* Response to JS Error
*/
onError?: (error: unknown) => void;
}

View File

@@ -54,7 +54,7 @@ export const UserLabel: FC<{
);
};
// TODO: 增加 show background 变体
// TODO: Added show background variant
export const UserName: FC<{
userUniqueName?: string;
className?: string;

View File

@@ -206,7 +206,7 @@ const FileCard: FC<IFileCardProps> = props => {
<div
// chat-uikit-file-card__progress-wrap
className={classNames(
// TODO: ui 补充进度条颜色
// TODO: ui supplement progress bar color
'coz-fg-hglt-dim absolute top-0 left-0 w-[280px] h-[72px]',
'chat-uikit-file-card-progress-animation',
)}

View File

@@ -24,27 +24,27 @@ import {
export interface IFileCardProps {
file: IFileInfo;
/**
* 用于识别成功 / 失败状态的key
* Key used to identify success/failure status
*/
attributeKeys: IFileAttributeKeys;
/**
* 文案配置
* copywriting configuration
*/
tooltipsCopywriting?: IFileCardTooltipsCopyWritingConfig;
/**
* 是否只读
* Is it read-only?
*/
readonly?: boolean;
/**
* 取消上传事件回调
* Cancel upload event callback
*/
onCancel: () => void;
/**
* 重试上传事件回调
* Retry upload event callback
*/
onRetry: () => void;
/**
* 拷贝url事件回调
* Copy URL event callback
*/
onCopy: () => void;
className?: string;

View File

@@ -57,7 +57,7 @@ export const FileContent: FC<IProps> = props => {
const { content_obj = safeJSONParse(message.content) } = message;
/**
* 判断是否为文件类型的卡片 或者 没有配置file属性config则拒绝使用该卡片
* Determine whether it is a card of file type, or refuse to use the card without configuring the file attribute config
*/
if (
!isFile(content_obj) ||
@@ -68,21 +68,21 @@ export const FileContent: FC<IProps> = props => {
}
/**
* 处理点击取消上传的事件
* Handle the event of clicking Cancel Upload
*/
const handleCancel = () => {
onCancel?.({ message, extra: {} });
};
/**
* 处理重试上传的事件
* Handling events that retry uploads
*/
const handleRetry = () => {
onRetry?.({ message, extra: {} });
};
/**
* 处理拷贝文件地址的事件
* Handling events that copy file addresses
*/
const handleCopy = (fileIndex?: number) => {
onCopy?.({ message, extra: { fileIndex } });

View File

@@ -44,7 +44,7 @@ export interface ContentBoxEvents {
}
export interface BaseContentBoxProps {
/** 是否在浏览器视窗内truefalse不在undefined未检测 */
/** Whether in the browser window, true: in, false: not, undefined: not detected */
inView?: boolean;
contentBoxEvents?: ContentBoxEvents;
}

View File

@@ -56,21 +56,21 @@ export const FileItemList: FC<FileItemListProps> = ({
showBackground,
}) => {
/**
* 处理点击取消上传的事件
* Handle the event of clicking Cancel Upload
*/
const handleCancel = () => {
onCancel?.({ message, extra: {} });
};
/**
* 处理重试上传的事件
* Handling events that retry uploads
*/
const handleRetry = () => {
onRetry?.({ message, extra: {} });
};
/**
* 处理拷贝文件地址的事件
* Handling events that copy file addresses
*/
const handleCopy = () => {
onCopy?.({ message, extra: {} });

View File

@@ -60,7 +60,7 @@ export const ImageItemList: FC<ImageItemListProps> = ({
/>
) : (
<div
// 这里借用了 messageBoxInner 的样式风格
// Here we borrow the style of messageBoxInner
className={typeSafeMessageBoxInnerVariants({
color: 'whiteness',
border: null,

View File

@@ -45,10 +45,10 @@ export type MultimodalContentProps = IImageMessageContentProps &
};
/**
* 这个组件并不单纯 实际上并不应该叫 Content
* This component is not simple and should not actually be called Content.
*/
// TODO: @liushuoyan 提供开关啊~~
// TODO: @liushuoyan provides the switch~~
export const MultimodalContent: React.FC<MultimodalContentProps> = ({
renderTextContentAddonTop,
message,
@@ -66,7 +66,7 @@ export const MultimodalContent: React.FC<MultimodalContentProps> = ({
}) => {
const { content_obj } = message;
if (!isMultimodalContentListLike(content_obj)) {
// TODO: broke 的消息应该需要加一个统一的兜底和上报
// TODO: Broke news should need to add a unified bottom line and report
return null;
}

View File

@@ -53,8 +53,8 @@ export const TextItemList: FC<TextItemListProps> = ({
return (
/**
* TODO: 由于目前设计不支持一条 message 渲染多个 content 这里需要借用一下发送消息的文字气泡背景色
* 目前只有用户才能发送 multimodal 消息
* TODO: Since the current design does not support one message to render multiple content, you need to borrow the text bubble background color of the sent message.
* Currently only users can send multimodal messages
*/
<div
className={typeSafeMessageBoxInnerVariants({

View File

@@ -61,7 +61,7 @@ const getMentionBotContent = ({
mentioned,
getBotInfo,
}: IPlainTextMessageContentProps) => {
// 接口真不一定返回了 mention_list
// The interface does not necessarily return mention_list
if (!mentioned) {
return '';
}

View File

@@ -64,7 +64,7 @@ export const SingleImageContentWithAutoSize: FC<
Boolean(sth && 'image_list' in { ...sth }),
}),
} = message;
// 类型守卫一般情况也不影响hooks的顺序问题
// Type guards generally do not affect the order of hooks
if (!isImage(content_obj)) {
return null;
}
@@ -74,10 +74,10 @@ export const SingleImageContentWithAutoSize: FC<
};
/**
* 这里这么做是有原因的
* 前端计算groupId是通过replyId分组服务端未ack前是localMessageId
* 因此服务端ack后会导致循环的key发生变化导致组件unmount -> mount(销毁重建)
* 因此需要用比较trick的方式来实现图片展示优化的问题
* There's a reason for this.
* The front-end compute groupId is grouped by replyId (localMessageId before server level is not ack)
* Therefore, after the server level ack, the key of the loop will change, causing the component to unmount - > mount (destroy and rebuild).
* Therefore, it is necessary to use a more trick way to achieve the problem of picture display optimization
*/
const blobImageMap: IBlobImageMap = {};
const isBlob = (url: string) => url?.startsWith('blob:');
@@ -89,7 +89,7 @@ const SingleImageContentWithAutoSizeImpl: FC<
const { imageAutoSizeContainerWidth = 0 } = useUiKitMessageBoxContext();
const localMessageId = message.extra_info.local_message_id;
// 目前服务端下发的图片 ori = thumb 因此目前用一个就行
// The picture sent by the current server level ori = thumb, so just use one for now.
const currentImageUrl = content_obj?.image_list?.at(0)?.image_ori?.url ?? '';
const { displayHeight, displayWidth, isCover } = getImageDisplayAttribute(

View File

@@ -26,26 +26,26 @@ import './index.less';
type IBlobImageMap = Record<string, string>;
/**
* 这里这么做是有原因的
* 前端计算groupId是通过replyId分组服务端未ack前是localMessageId
* 因此服务端ack后会导致循环的key发生变化导致组件unmount -> mount(销毁重建)
* 因此需要用比较trick的方式来实现图片展示优化的问题
* There's a reason for this.
* The front-end compute groupId is grouped by replyId (localMessageId before server level is not ack)
* Therefore, after the server level ack, the key of the loop will change, causing the component to unmount - > mount (destroy and rebuild).
* Therefore, it is necessary to use a more trick way to achieve the problem of picture display optimization
*/
const blobImageMap: IBlobImageMap = {};
const isBlob = (url: string) => url?.startsWith('blob:');
/**
* @deprecated 废弃不再维护,请尽快迁移至 SingleImageContentWithAutoSize 组件
* @Deprecated is no longer maintained, please migrate to SingleImageContentWithAutoSize component as soon as possible
*/
export const SingleImageContent: FC<IImageMessageContentProps> = props => {
const { message, onImageClick } = props;
// @liushuoyan 这里类型大溃败,引入了 any
// @Liushuoyan here type rout, introduced any
const { content_obj = safeJSONParse(message.content) } = message;
const localMessageId = message.extra_info.local_message_id;
// 目前服务端下发的图片 ori = thumb 因此目前用一个就行
// The picture sent by the current server level ori = thumb, so just use one for now.
const currentImageUrl = content_obj?.image_list?.at(0)?.image_ori?.url ?? '';
if (isBlob(currentImageUrl)) {

View File

@@ -42,7 +42,7 @@ export const SingleImageContentUI: React.FC<SingleImageContentUIProps> = ({
src={thumbUrl || EmptyImage}
className="chat-uikit-single-image-content__image"
/**
* 这里不采用 semi Image 组件自带的 preview 功能。传入的 onImageClick 回调中有副作用会拉起 preview 组件
* The preview function that comes with the semi Image component is not used here. There are side effects in the incoming onImageClick callback that will pull the preview component
*/
preview={false}
/>

View File

@@ -55,7 +55,7 @@ export type CozeImageProps = MdBoxImageProps & {
const TIME_OUT = 10000;
// TODO: @liushuoyan 看看这里能不能搞一个插槽之类的东西
// TODO: @liushuoyan see if you can get a slot or something here
// eslint-disable-next-line @typescript-eslint/naming-convention, @coze-arch/max-line-per-function
export const _CozeImage: FC<CozeImageProps> = props => {
const [showPreview, setShowPreview] = useState(false);
@@ -110,7 +110,7 @@ export const _CozeImage: FC<CozeImageProps> = props => {
const originImageInfoRef = useRef<OriginImageInfo>({});
const [imageStyles, setImageStyles] = useState<CSSProperties>({});
// 加载状态
// loading status
const [loading, setLoading] = useState(true);
const [imageSrc, setImageSrc] = useState(DefaultImage);

View File

@@ -48,7 +48,7 @@ const isCocoLink = (link: string) => {
return parsedLink.protocol === 'coco:';
};
/** 被链接元素替换成的组件 */
/** Components replaced by linked elements */
export const CozeLink: FC<
MdBoxLinkProps & {
onLinkElementEnter?: (params: {

View File

@@ -18,7 +18,7 @@ import { createContext, useContext } from 'react';
import { type IEventCallbacks } from '@coze-common/chat-uikit-shared';
/**
* 为了支持CozeImage隔空取物和性能优化角度考虑临时开的Context没事儿别乱用。。。。
* In order to support CozeImage's empty fetching and performance optimization, consider the temporarily opened Context, don't use it indiscriminately...
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
export const OnboardingContext = createContext<{

View File

@@ -101,7 +101,7 @@ export const useAudioRecordInteraction = ({
return;
}
const { clientX, clientY } = getClientPosition(eventType);
// 获取元素的边界信息
// Get the boundary information of the element
const rect = activeZoneElement.getBoundingClientRect();
const isOutRect =
clientX < rect.left ||
@@ -109,7 +109,7 @@ export const useAudioRecordInteraction = ({
clientY < rect.top ||
clientY > rect.bottom;
// 判断触摸点是否在元素范围内
// Determine whether the touch point is within the scope of the element
if (isOutRect && !isMoveLeave.current) {
isMoveLeave.current = true;
onMoveLeaveRef.current?.();
@@ -201,7 +201,7 @@ export const useAudioRecordInteraction = ({
});
useEffect(
() => () => {
// 避免异常情况下事件无法卸载
// Avoid events that cannot be uninstalled under abnormal circumstances
document.removeEventListener('mousemove', onPointerMove);
document.removeEventListener('touchmove', onPointerMove);
},

View File

@@ -33,7 +33,7 @@ export const useObserveCardContainer = ({
}) => {
const eventCenter = useUiKitEventCenter();
/** 30s 内没变化则自动清除 observer */
/** If there is no change within 30s, the observer will be automatically cleared. */
const debouncedDisconnect = useDebounceFn(
(getResizeObserver: () => ResizeObserver | null) => {
const resizeObserver = getResizeObserver();

View File

@@ -20,7 +20,7 @@ export const formatMessageBoxContentTime = (contentTime: number): string => {
if (contentTime < 1) {
return '';
}
// 当天:hh:mm;跨天:mm-dd hh:mm;跨年:yyyy-mm-dd hh:mm
// Day: hh: mm; across the sky: mm-dd hh: mm; New Year's Eve: yyyy-mm-dd hh: mm
const now = Date.now();
const today = dayjs(now);
const messageDay = dayjs(contentTime);

View File

@@ -19,17 +19,17 @@ export const getImageDisplayAttribute = (
height: number,
contentWidth: number,
) => {
// 图片比例
// image scale
const imageRatio = width / height;
// 展示宽度
// display width
let displayWidth = contentWidth;
// 展示高度
// display height
let displayHeight = contentWidth / imageRatio;
// 是否裁切
// Whether to cut
let isCover = false;
// (小尺寸图)
// (Small size drawing)
if (width <= contentWidth && height <= 240) {
displayWidth = width;
@@ -38,16 +38,16 @@ export const getImageDisplayAttribute = (
displayWidth = contentWidth;
displayHeight = 120;
isCover = true;
// (长竖图)图片宽度:图片高度 <= 0.5
// (Long vertical) Image width: Image height < = 0.5
} else if (imageRatio <= 0.5) {
displayWidth = 120;
displayHeight = 240;
isCover = true;
// (等比展示图)
// (Equivalent display picture)
} else if (0.5 <= imageRatio && imageRatio <= contentWidth / 240) {
displayWidth = 240 * imageRatio;
displayHeight = 240;
// (中长横图)
// (Medium and long horizontal chart)
} else if (
contentWidth / 240 <= imageRatio &&
imageRatio <= contentWidth / 240

View File

@@ -15,8 +15,8 @@
*/
/**
* 是否是在苹果平台的Webkit内核浏览器下
* 这个判断条件不等于是在苹果设备下因为部分苹果设备例如Mac可以运行非原生Webkit引擎的浏览器例如Chromium(Blink)
* Is it under the Webkit kernel browser of the Apple platform?
* Note: This judgment condition is not equal to under Apple devices, because some Apple devices (such as Mac) can run non-native Webkit engine browsers, such as Chromium (Blink)
*/
export const isAppleWebkit = () =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@@ -26,5 +26,5 @@ type StoreStructRange = Record<keyof StoreStruct, boolean | string | number>;
const storeStructRangeCheck = (range: StoreStructRange) => 0;
declare const voidStruct: StoreStruct;
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- 类型测试
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- type testing
const dryRun = () => storeStructRangeCheck(voidStruct);

View File

@@ -18,8 +18,8 @@ import Browser from 'bowser';
let getIsMobileCache: boolean | undefined;
/**
* 是否是移动设备
* ipad 不是移动设备
* Is it a mobile device?
* Note: iPad is not a mobile device
*/
const isMobile = () =>
Browser.getParser(navigator.userAgent)
@@ -35,7 +35,7 @@ export const getIsMobile = () => {
let getIsIPhoneOrIPadCache: boolean | undefined;
/**
* gpt-4 提供的代码
* Code provided by gpt-4
*/
export const getIsIPhoneOrIPad = () => {
if (typeof getIsIPhoneOrIPadCache === 'undefined') {
@@ -53,7 +53,7 @@ export const getIsIPhoneOrIPad = () => {
let getIsIPadCache: boolean | undefined;
/**
* gpt-4 提供的代码
* Code provided by gpt-4
*/
export const getIsIPad = () => {
if (typeof getIsIPadCache === 'undefined') {

View File

@@ -15,7 +15,7 @@
*/
/**
* @deprecated 非常非常坏,尽快换为 typeSafeJsonParse
* @Deprecated very very bad, change to typeSafeJsonParse ASAP.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const safeJSONParse: (v: any, emptyValue?: any) => any = (