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

@@ -3,21 +3,21 @@ const fs = require('fs');
const ROOT_DIR = process.cwd();
// 工具函数 aa-bb-cc -> AaBbCc
// Tool function aa-bb-cc - > AaBbCc
const getPascalName = name =>
name
.split('-')
.map(s => s.slice(0, 1).toUpperCase() + s.slice(1))
.join('');
// 工具函数 aa-bb-cc -> aaBbCc
// Tool function aa-bb-cc - > aaBbCc
const getCamelName = name =>
name
.split('-')
.map((s, i) => (i === 0 ? s : s.slice(0, 1).toUpperCase() + s.slice(1)))
.join('');
// 工具函数 aa-bb-cc -> AA_BB_CC
// Tool function aa-bb-cc - > AA_BB_CC
const getConstantName = name =>
name
.split('-')
@@ -25,30 +25,30 @@ const getConstantName = name =>
.join('_');
module.exports = plop => {
// 注册一个新的动作,用于在导出文件和注册文件中添加新的节点注册信息
// Register a new action to add new node registration information in the export and registration files
plop.setActionType('registryNode', async answers => {
const { name, pascalName, supportTest } = answers;
const constantName = getConstantName(name);
const registryName = `${constantName}_NODE_REGISTRY`;
// 修改导出文件
// Modify the export file
const nodeExportFilePath = './src/node-registries/index.ts';
const nodeContent = fs.readFileSync(nodeExportFilePath, 'utf8');
const nodeContentNew = nodeContent.replace(
'// cli 脚本插入标识registry请勿修改/删除此行注释',
`export { ${registryName} } from './${name}';
// cli 脚本插入标识registry请勿修改/删除此行注释`,
// The cli script inserts the identifier (registry), please do not modify/delete this line comment `,
);
fs.writeFileSync(nodeExportFilePath, nodeContentNew, 'utf8');
// 修改注册文件
// Modify registration documents
const nodeRegistryFilePath = './src/nodes-v2/constants.ts';
const nodeRegistryContent = fs.readFileSync(nodeRegistryFilePath, 'utf8');
const nodeRegistryContentNew = nodeRegistryContent
.replace(
'// cli 脚本插入标识import请勿修改/删除此行注释',
`${registryName},
// cli 脚本插入标识import请勿修改/删除此行注释`,
// The cli script inserts the identity (import), please do not modify/delete this line comment `,
)
.replace(
'// cli 脚本插入标识registry请勿修改/删除此行注释',
@@ -57,7 +57,7 @@ module.exports = plop => {
);
fs.writeFileSync(nodeRegistryFilePath, nodeRegistryContentNew, 'utf8');
// 修改 node-content 注册文件
// Modify the node-content registration file
const nodeContentRegistryFilePath =
'./src/components/node-render/node-render-new/content/index.tsx';
const nodeContentRegistryContent = fs.readFileSync(
@@ -69,12 +69,12 @@ module.exports = plop => {
.replace(
'// cli 脚本插入标识import请勿修改/删除此行注释',
`import { ${pascalName}Content } from '@/node-registries/${name}';
// cli 脚本插入标识import请勿修改/删除此行注释`,
// The cli script inserts the identity (import), please do not modify/delete this line comment `,
)
.replace(
'// cli 脚本插入标识registry请勿修改/删除此行注释',
`[StandardNodeType.${pascalName}]: ${pascalName}Content,
// cli 脚本插入标识registry请勿修改/删除此行注释`,
// The cli script inserts the identifier (registry), please do not modify/delete this line comment `,
);
fs.writeFileSync(
nodeContentRegistryFilePath,
@@ -82,7 +82,7 @@ module.exports = plop => {
'utf8',
);
// 如果节点无需支持单节点测试,删除 node-test 文件
// If the node does not need to support single-node testing, delete the node-test file
const testFilePath = path.resolve(
ROOT_DIR,
`./src/node-registries/${name}/node-test.ts`,
@@ -94,7 +94,7 @@ module.exports = plop => {
return `节点 ${name} 已注册`;
});
// 注册一个新的生成器,用于创建新的节点目录和文件
// Register a new generator for creating new node directories and files
plop.setGenerator('create node', {
description: 'generate template',
prompts: [

View File

@@ -111,7 +111,7 @@ export default function useRelated({
const isBot = relatedEntityValue?.type === IntelligenceType.Bot;
// 由于分页限制 选中的 botId 可能找不到对应的 option 需要额外添加
// Due to paging restrictions, the selected botId may not find the corresponding option and needs to be added
const extraBotOption = useExtraBotOption(
baseRelatedEntities,
relatedEntityValue?.id,

View File

@@ -191,7 +191,7 @@ export const Bots: React.FC<BotsProps & DisableExtraOptions> = ({
onChange?.(_value);
};
// 由于分页限制 选中的 botId 可能找不到对应的 option 需要额外添加
// Due to paging restrictions, the selected botId may not find the corresponding option and needs to be added
const extraBotOption = useExtraBotOption(
selectList,
idValue,
@@ -199,7 +199,7 @@ export const Bots: React.FC<BotsProps & DisableExtraOptions> = ({
handleChange,
);
// 接口得到的总数并非真实的总数,前端可能会拼接 options
// The total number obtained by the interface is not the real total, and the front end may splice options.
const listMaxHeight = useMemo(() => {
const realTotal = extraBotOption ? searchTotal + 1 : searchTotal;
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
@@ -216,7 +216,7 @@ export const Bots: React.FC<BotsProps & DisableExtraOptions> = ({
{ disableBot, disableProject },
findItem?.type,
);
// 禁用状态下清空选中值
// Clear the selected value when disabled
if (value && findItem?.type && disabled) {
onChange?.(undefined);
}
@@ -236,11 +236,11 @@ export const Bots: React.FC<BotsProps & DisableExtraOptions> = ({
setIsLoading(true);
}
if (isReset) {
// 如果是重新搜索,需要清空上一次的游标
// If it is a re-search, you need to clear the previous cursor
nextCursorRef.current = undefined;
}
// project 内部使用新接口查询列表
// Query the list using the new interface within the project
const res = await intelligenceApi.GetDraftIntelligenceList({
space_id:
globalState.spaceId === PUBLIC_SPACE_ID
@@ -313,7 +313,7 @@ export const Bots: React.FC<BotsProps & DisableExtraOptions> = ({
itemSize: 32,
}}
onChange={newValue => {
// 设置选中项类型bot / project
// Set the selected item type (bot/project)
const findItem = selectList.find(item => item.value === newValue);
if (useNewGlobalVariableCache && findItem?.type) {

View File

@@ -69,7 +69,7 @@ export const BotProjectSelect: React.FC<BotSelectProps> = ({
...props
}) => {
let value = originValue;
// 兼容历史数据
// Compatible with historical data
if (typeof originValue === 'string') {
value = {
id: originValue,
@@ -109,7 +109,7 @@ export const BotProjectSelect: React.FC<BotSelectProps> = ({
(hasVariableNode || hasVariableAssignNode) &&
!hideVariblesForce;
// 试运行都是临时搞了一个会话,这里把 bot 聊天历史展示可能会误导,先隐藏
// Practice running is a temporary session, here the bot chat history display may be misleading, first hide
const showChatHistory = false;
// const showChatHistory =
// botSelected && !isChatHistoryLoading && hasChatHistoryEnabledLLM;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
//后端无定义 根据 bot_info 中的 workflow_info.profile_memory 推导而来
//Backend undefined, derived from workflow_info profile_memory in bot_info
import { type IntelligenceType } from '@coze-arch/idl/intelligence_api';
export interface Variable {

View File

@@ -43,12 +43,12 @@ export const useBotInfo = (botId?: string) => {
return { isLoading, botInfo };
};
// 为 wf 使用 bot 信息做数据转换
// Data conversion using bot information for wf
export const transformBotInfo = {
// 模型数据
// model data
model: (data?: GetDraftBotInfoAgwData): ModelInfo =>
data?.bot_info?.model_info ?? {},
// 基本信息数据
// Basic information data
basicInfo: (
botInfo?: GetDraftBotInfoAgwData,
): IBotSelectOption | undefined => {
@@ -62,10 +62,10 @@ export const transformBotInfo = {
type: IntelligenceType.Bot,
};
},
// 数据库信息
// database information
database: (botInfo?: GetDraftBotInfoAgwData): BotTable[] | undefined =>
botInfo?.bot_info?.database_list,
// 变量信息
// Variable information
variable: (botInfo?: GetDraftBotInfoAgwData) =>
botInfo?.bot_info?.variable_list,
};

View File

@@ -28,7 +28,7 @@ export const useLTMInfo = (botId?: string) => {
);
return {
// 是否开启长期记忆
// Is long-term memory switched on?
ltmEnabled: timeCapsuleMode === TimeCapsuleMode.On,
isLoading,
};

View File

@@ -18,7 +18,7 @@ import { type BotTable, BotTableRWMode } from '@coze-arch/bot-api/memory';
import { transformBotInfo, useBotInfo } from './use-bot-info';
// 多人模式下产品希望前端展示uuid & id 目前这两个字段会被后端过滤 先由前端补充这两个字段 后端充分评估过再移除过滤逻辑
// In multiplayer mode, the product hopes that the front-end will display uuid & id. At present, these two fields will be filtered by the back-end. The front-end will supplement these two fields first, and the back-end will fully evaluate and then remove the filtering logic.
function addUidAndIdToBotFieldsIfIsUnlimitedReadWriteMode(
tableInfo: BotTable[],
): BotTable[] {

View File

@@ -122,10 +122,10 @@ export const Bots: React.FC<BotsProps> = ({ value, onChange, ...props }) => {
const [searchTotal, setTotal] = useState(0);
const containerRef = useRef<HTMLDivElement>(null);
// 由于分页限制 选中的botId可能找不到对应的option 需要额外添加
// Due to paging restrictions, the selected botId may not find the corresponding option and needs to be added
const extraBotOption = useExtraBotOption(selectList, value);
// 接口得到的总数并非真实的总数,前端可能会拼接 options
// The total number obtained by the interface is not the real total, and the front end may splice options.
const listMaxHeight = useMemo(() => {
const realTotal = extraBotOption ? searchTotal + 1 : searchTotal;
// eslint-disable-next-line @typescript-eslint/no-magic-numbers

View File

@@ -39,7 +39,7 @@ interface BotSelectProps {
hideVariblesForce?: boolean;
hasVariableNode?: boolean;
hasVariableAssignNode?: boolean;
/** @deprecated 这个字段目前没有用了,后续可以清理掉 */
/** @Deprecated This field is currently useless, it can be cleaned up later. */
hasDatabaseNode?: boolean;
hasLTMNode?: boolean;
hasChatHistoryEnabledLLM?: boolean;
@@ -74,7 +74,7 @@ export const BotSelect: React.FC<BotSelectProps> = ({
!isVariablesLoading &&
(hasVariableNode || hasVariableAssignNode) &&
!hideVariblesForce;
// 试运行都是临时搞了一个会话,这里把 bot 聊天历史展示可能会误导,先隐藏
// Practice running is a temporary session, here the bot chat history display may be misleading, first hide
const showChatHistory = false;
// const showChatHistory =
// botSelected && !isChatHistoryLoading && hasChatHistoryEnabledLLM;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
//后端无定义 根据bot_info中的workflow_info.profile_memory推导而来
//Backend undefined, derived from workflow_info profile_memory in bot_info
export interface Variable {
key: string;
description?: string;

View File

@@ -42,12 +42,12 @@ export const useBotInfo = (botId?: string) => {
return { isLoading, botInfo };
};
// 为wf使用bot信息做数据转换
// Data conversion using bot information for wf
export const transformBotInfo = {
// 模型数据
// model data
model: (data?: GetDraftBotInfoAgwData): ModelInfo =>
data?.bot_info?.model_info ?? {},
// 基本信息数据
// Basic information data
basicInfo: (
botInfo?: GetDraftBotInfoAgwData,
): IBotSelectOption | undefined => {
@@ -60,10 +60,10 @@ export const transformBotInfo = {
value: botInfo?.bot_info?.bot_id ?? '',
};
},
// 数据库信息
// database information
database: (botInfo?: GetDraftBotInfoAgwData): BotTable[] | undefined =>
botInfo?.bot_info?.database_list,
// 变量信息
// Variable information
variable: (botInfo?: GetDraftBotInfoAgwData) =>
botInfo?.bot_info?.variable_list,
};

View File

@@ -28,7 +28,7 @@ export const useLTMInfo = (botId?: string) => {
);
return {
// 是否开启长期记忆
// Is long-term memory switched on?
ltmEnabled: timeCapsuleMode === TimeCapsuleMode.On,
isLoading,
};

View File

@@ -18,7 +18,7 @@ import { type BotTable, BotTableRWMode } from '@coze-arch/bot-api/memory';
import { transformBotInfo, useBotInfo } from './use-bot-info';
// 多人模式下产品希望前端展示uuid & id 目前这两个字段会被后端过滤 先由前端补充这两个字段 后端充分评估过再移除过滤逻辑
// In multiplayer mode, the product hopes that the front-end will display uuid & id. At present, these two fields will be filtered by the back-end. The front-end will supplement these two fields first, and the back-end will fully evaluate and then remove the filtering logic.
function addUidAndIdToBotFieldsIfIsUnlimitedReadWriteMode(
tableInfo: BotTable[],
): BotTable[] {

View File

@@ -24,9 +24,9 @@ import { EditorView } from '@codemirror/view';
import { cozeLight } from './themes/coze-light';
import { cozeDark } from './themes/coze-dark';
// 注册语言
// Registration language
languages.register('json', {
// mixLanguages 用于解决 「插值也使用了括号,导致无法正确高亮」的问题
// MixLanguages is used to solve the problem of "interpolation also uses parentheses, which makes it impossible to highlight correctly"
language: mixLanguages({
outerLanguage: json.language,
}),
@@ -35,7 +35,7 @@ languages.register('json', {
languages.register('shell', shell);
// 注册主题
// Register a topic
themes.register('coze-light', cozeLight);
themes.register('coze-dark', cozeDark);

View File

@@ -56,7 +56,7 @@ const lineHeightOption = (value?: string | number) =>
const extensions = [
mixLanguages({}),
RawEditorTheme,
// ...其他 extensions
// ... Other extensions
history(),
keymap.of([...defaultKeymap, ...historyKeymap]),
];

View File

@@ -20,7 +20,7 @@ import type { CommentEditorCommand } from '../type';
import type { CommentEditorModel } from '../model';
import { CommentEditorBlockFormat } from '../constant';
// 定义块前缀模式和对应的格式
// Define the block prefix pattern and corresponding format
const blockPrefixConfig: Array<[RegExp, CommentEditorBlockFormat]> = [
[/^#$/, CommentEditorBlockFormat.HeadingOne],
[/^##$/, CommentEditorBlockFormat.HeadingTwo],
@@ -31,14 +31,14 @@ const blockPrefixConfig: Array<[RegExp, CommentEditorBlockFormat]> = [
[/^1\.$/, CommentEditorBlockFormat.NumberedList],
];
// 删除文本的函数
// Function to delete text
const deleteText = (model: CommentEditorModel, text: string): void => {
Array.from(text).forEach(() => {
Editor.deleteBackward(model.editor, { unit: 'character' });
});
};
// 处理块前缀的函数
// Functions that handle block prefixes
const handleBlockPrefix = (
model: CommentEditorModel,
text: string,
@@ -60,7 +60,7 @@ const handleBlockPrefix = (
export const blockPrefixCommand: CommentEditorCommand = {
key: ' ',
exec: ({ model, event }) => {
// 检查是否正在输入拼音
// Check if pinyin is being entered.
if (event.nativeEvent.isComposing) {
return;
}

View File

@@ -21,7 +21,7 @@ export const clearFormatEnterCommand: CommentEditorCommand = {
key: 'Enter',
shift: false,
exec: ({ model, event }) => {
// 检查是否正在输入拼音
// Check if pinyin is being entered.
if (event.nativeEvent.isComposing) {
return;
}

View File

@@ -41,7 +41,7 @@ export const BlankArea: FC<IBlankArea> = props => {
e.stopPropagation();
model.setFocus(false);
selectNode(e);
playground.node.focus(); // 防止节点无法被删除
playground.node.focus(); // Prevent nodes from being deleted
}}
onClick={e => {
model.setFocus(true);

View File

@@ -41,51 +41,51 @@ export const BorderArea: FC<IBorderArea> = props => {
return (
<div className="workflow-comment-border-area z-[999]">
{/* 左边 */}
{/* Left */}
<DragArea
className="left-[-10px] top-[10px] w-[20px] h-[calc(100%-20px)]"
model={model}
/>
{/* 右边 */}
{/* Right */}
<DragArea
className={classNames('right-[-10px] top-[10px] h-[calc(100%-20px)]', {
'w-[10px]': overflow, // 防止遮挡滚动条
'w-[10px]': overflow, // Prevent occlusion of scroll bars
'w-[20px]': !overflow,
})}
model={model}
/>
{/* 上边 */}
{/* above */}
<DragArea
className="top-[-10px] left-[10px] w-[calc(100%-20px)] h-[20px]"
model={model}
/>
{/* 下边 */}
{/* below */}
<DragArea
className="bottom-[-10px] left-[10px] w-[calc(100%-20px)] h-[20px]"
model={model}
/>
{/** 左上角 */}
{/** upper left corner */}
<ResizeArea
className="left-0 top-0 cursor-nwse-resize"
model={model}
getDelta={({ x, y }) => ({ top: y, right: 0, bottom: 0, left: x })}
onResize={onResize}
/>
{/** 右上角 */}
{/** upper right corner */}
<ResizeArea
className="right-0 top-0 cursor-nesw-resize"
model={model}
getDelta={({ x, y }) => ({ top: y, right: x, bottom: 0, left: 0 })}
onResize={onResize}
/>
{/** 右下角 */}
{/** lower right corner */}
<ResizeArea
className="right-0 bottom-0 cursor-nwse-resize"
model={model}
getDelta={({ x, y }) => ({ top: 0, right: x, bottom: y, left: 0 })}
onResize={onResize}
/>
{/** 左下角 */}
{/** Lower left corner */}
<ResizeArea
className="left-0 bottom-0 cursor-nesw-resize"
model={model}

View File

@@ -28,10 +28,10 @@ export const CommentContainer: FC<ICommentContainer> = props => {
const { focused, children, style } = props;
const scrollbarStyle = {
// 滚动条样式
// scroll bar style
scrollbarWidth: 'thin',
scrollbarColor: 'rgb(159 159 158 / 65%) transparent',
// 针对 WebKit 浏览器(如 ChromeSafari)的样式
// Styles for WebKit browsers such as Chrome, Safari
'&::-webkit-scrollbar': {
width: '4px',
},
@@ -56,7 +56,7 @@ export const CommentContainer: FC<ICommentContainer> = props => {
)}
data-flow-editor-selectable="false"
style={{
// tailwind 不支持 outline 的样式,所以这里需要使用 style 来设置
// Tailwind does not support the style of outline, so you need to use style to set it here.
outline: focused ? '1px solid #FF811A' : '1px solid #F2B600',
...scrollbarStyle,
...style,

View File

@@ -39,7 +39,7 @@ export const ContentDragArea: FC<IContentDragArea> = props => {
const [active, setActive] = useState(false);
useEffect(() => {
// 当编辑器失去焦点时,取消激活状态
// When the editor loses focus, deactivate it
if (!focused) {
setActive(false);
}
@@ -68,7 +68,7 @@ export const ContentDragArea: FC<IContentDragArea> = props => {
mouseDownEvent.stopPropagation();
model.setFocus(false);
selectNode(mouseDownEvent);
playground.node.focus(); // 防止节点无法被删除
playground.node.focus(); // Prevent nodes from being deleted
const startX = mouseDownEvent.clientX;
const startY = mouseDownEvent.clientY;
@@ -76,10 +76,10 @@ export const ContentDragArea: FC<IContentDragArea> = props => {
const handleMouseUp = (mouseMoveEvent: MouseEvent) => {
const deltaX = mouseMoveEvent.clientX - startX;
const deltaY = mouseMoveEvent.clientY - startY;
// 判断是拖拽还是点击
// Determine whether to drag or click
const delta = 5;
if (Math.abs(deltaX) < delta && Math.abs(deltaY) < delta) {
// 点击后隐藏
// Hide after clicking
setActive(true);
}
document.removeEventListener('mouseup', handleMouseUp);

View File

@@ -59,7 +59,7 @@ export const DragArea: FC<IDragArea> = props => {
model.setFocus(false);
onStartDrag(e);
selectNode(e);
playground.node.focus(); // 防止节点无法被删除
playground.node.focus(); // Prevent nodes from being deleted
}}
onFocus={onFocus}
onBlur={onBlur}

View File

@@ -21,17 +21,17 @@ export const Block = ({ attributes, children, element }) => {
textAlign: element.align,
color: 'var(--coz-fg-primary, rgba(6, 7, 9, 0.80))',
};
// 根据元素类型选择对应的 HTML 标签
// Select the corresponding HTML tag based on the element type
switch (element.type) {
case CommentEditorBlockFormat.Paragraph:
// 渲染段落
// Render paragraph
return (
<p className="text-[12px] m-0 p-0" style={style} {...attributes}>
{children}
</p>
);
case CommentEditorBlockFormat.Blockquote:
// 渲染引用块
// Render reference block
return (
<blockquote
className="border-l-[3px] border-t-0 border-b-0 border-r-0 border-solid border-[#ced0d4] m-0 p-0 pl-[8px] ml-[8px] text-[12px]"
@@ -45,7 +45,7 @@ export const Block = ({ attributes, children, element }) => {
</blockquote>
);
case CommentEditorBlockFormat.HeadingOne:
// 渲染一级标题
// Render first-level title
return (
<h1
className="text-[18px] mx-0 my-[6px] p-0 font-[600]"
@@ -56,7 +56,7 @@ export const Block = ({ attributes, children, element }) => {
</h1>
);
case CommentEditorBlockFormat.HeadingTwo:
// 渲染二级标题
// Render secondary title
return (
<h2
className="text-[16px] mx-0 my-[6px] p-0 font-[600]"
@@ -67,7 +67,7 @@ export const Block = ({ attributes, children, element }) => {
</h2>
);
case CommentEditorBlockFormat.HeadingThree:
// 渲染三级标题
// Render three-level title
return (
<h3
className="text-[14px] mx-0 my-[6px] p-0 font-[600]"
@@ -78,7 +78,7 @@ export const Block = ({ attributes, children, element }) => {
</h3>
);
case CommentEditorBlockFormat.BulletedList:
// 渲染无序列表
// Render unordered list
return (
<ul
className="text-[12px] m-0 p-0 pl-[16px] font-[400]"
@@ -89,7 +89,7 @@ export const Block = ({ attributes, children, element }) => {
</ul>
);
case CommentEditorBlockFormat.NumberedList:
// 渲染有序列表
// Render ordered list
return (
<ol
className="text-[12px] m-0 p-0 pl-[16px]"
@@ -100,14 +100,14 @@ export const Block = ({ attributes, children, element }) => {
</ol>
);
case CommentEditorBlockFormat.ListItem:
// 渲染列表项
// Render list items
return (
<li className="text-[12px] m-0 p-0" style={style} {...attributes}>
{children}
</li>
);
default:
// 默认渲染为段落
// Default render as paragraph
return (
<p className="text-[12px] m-0 p-0" style={style} {...attributes}>
{children}

View File

@@ -45,7 +45,7 @@ export const CommentEditor: FC<ICommentEditor> = props => {
const renderBlock = useCallback(blockProps => <Block {...blockProps} />, []);
const renderLeaf = useCallback(leafProps => <Leaf {...leafProps} />, []);
// 同步编辑器内部值变化
// Synchronize editor internal value changes
useEffect(() => {
const dispose = model.on<CommentEditorEvent.Change>(
CommentEditorEvent.Change,

View File

@@ -43,13 +43,13 @@ export const Leaf = ({ attributes, children, leaf }) => {
e.stopPropagation();
const link: string = leaf[CommentEditorLeafFormat.Link];
if (link === CommentDefaultLink) {
// 如果链接为默认链接,直接打开
// If the link is the default link, open it directly.
return window.open(link, '_blank');
} else if (/^([a-zA-Z][a-zA-Z\d+\-.]*):\/\//.test(link)) {
// 如果已经包含合法的协议,直接打开
// If a legal agreement is already included, open it directly
return window.open(link, '_blank');
} else {
// 如果没有合法协议,添加 https 协议头
// If there is no legal agreement, add the https protocol header.
// cp-disable-next-line
return window.open(`https://${link}`, '_blank');
}

View File

@@ -26,7 +26,7 @@ export const Placeholder: FC<RenderPlaceholderProps> = props => {
className="workflow-comment-editor-placeholder text-[12px] text-[var(--coz-fg-dim)] overflow-hidden absolute pointer-events-none w-full select-none decoration-clone"
style={
{
// 覆盖 slate 内置样式
// Override slate built-in style
}
}
>

View File

@@ -64,7 +64,7 @@ export const CommentRender: FC<{
onMouseEnter={updateOverflow}
onMouseDown={e => {
setTimeout(() => {
// 防止 selectNode 拦截事件,导致 slate 编辑器无法聚焦
// Prevent selectNode from intercepting events, causing the slate editor to fail to focus
selectNode(e);
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- delay
}, 20);
@@ -77,7 +77,7 @@ export const CommentRender: FC<{
>
<Form control={formControl}>
<>
{/* 背景 */}
{/* background */}
<CommentContainer focused={focused} style={{ height }}>
<ErrorBoundary
fallback={
@@ -89,21 +89,21 @@ export const CommentRender: FC<{
<Field name={CommentEditorFormField.Note}>
{({ field }: FieldRenderProps<string>) => (
<>
{/** 编辑器 */}
{/** editor */}
<CommentEditor
model={model}
value={field.value}
onChange={field.onChange}
/>
{/* 空白区域 */}
{/* blank space */}
<BlankArea model={model} />
{/* 内容拖拽区域(点击后隐藏) */}
{/* content drag and drop area (hidden after clicking) */}
<ContentDragArea
model={model}
focused={focused}
overflow={overflow}
/>
{/* 工具栏 */}
{/* toolbar */}
<CommentToolbarContainer
model={model}
containerRef={nodeRef}
@@ -113,7 +113,7 @@ export const CommentRender: FC<{
</Field>
</ErrorBoundary>
</CommentContainer>
{/* 边框 */}
{/* border */}
<BorderArea model={model} overflow={overflow} onResize={onResize} />
</>
</Form>

View File

@@ -60,7 +60,7 @@ export const ResizeArea: FC<IResizeArea> = props => {
const { resizing, resizeEnd } = onResize();
model.setFocus(false);
selectNode(mouseDownEvent);
playground.node.focus(); // 防止节点无法被删除
playground.node.focus(); // Prevent nodes from being deleted
const startX = mouseDownEvent.clientX;
const startY = mouseDownEvent.clientY;

View File

@@ -37,10 +37,10 @@ export const useActivate = (params: {
[],
);
// 清理 debounce
// Clean up the debounce
useEffect(() => () => setActivated.cancel(), [setActivated]);
// 监听处理 model 事件
// Listening for handling model events
useEffect(() => {
const eventHandlers = {
[CommentEditorEvent.MultiSelect]: () => setActivated(true),
@@ -58,7 +58,7 @@ export const useActivate = (params: {
};
}, [model, setActivated]);
// 鼠标事件处理
// mouse event handling
useEffect(() => {
const mouseHandler = (e: MouseEvent) => {
if (

View File

@@ -91,7 +91,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
e.stopPropagation();
}}
>
{/** 文本样式 */}
{/** Text style */}
<Dropdown
position="bottom"
trigger="hover"
@@ -106,7 +106,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
'0px 8px 24px 0px rgba(0, 0, 0, 0.16), 0px 16px 48px 0px rgba(0, 0, 0, 0.08)',
}}
>
{/** 正文 */}
{/** text */}
<div
className={classNames(textItemClass, {
[textItemMarkedClass]: model.isBlockMarked(
@@ -120,7 +120,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
<IconCozTextStyle className="text-xxl" />
<p>{I18n.t('workflow_note_main_text')}</p>
</div>
{/** 一级标题 */}
{/** first-level title */}
<div
className={classNames(textItemClass, {
[textItemMarkedClass]: model.isBlockMarked(
@@ -134,7 +134,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
<IconCozH1 className="text-xxl" />
<p>{I18n.t('workflow_note_heading')} 1</p>
</div>
{/** 二级标题 */}
{/** secondary title */}
<div
className={classNames(textItemClass, {
[textItemMarkedClass]: model.isBlockMarked(
@@ -148,7 +148,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
<IconCozH2 className="text-xxl" />
<p>{I18n.t('workflow_note_heading')} 2</p>
</div>
{/** 三级标题 */}
{/** third-level title */}
<div
className={classNames(textItemClass, {
[textItemMarkedClass]: model.isBlockMarked(
@@ -173,7 +173,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
<div className={separatorClass} />
{/** 粗体 */}
{/** bold */}
<Tooltip
content={I18n.t('workflow_note_bold')}
mouseEnterDelay={tooltipDelay}
@@ -192,7 +192,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
</span>
</Tooltip>
{/** 斜体 */}
{/** Italic */}
<Tooltip
content={I18n.t('workflow_note_italic')}
mouseEnterDelay={tooltipDelay}
@@ -211,7 +211,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
</span>
</Tooltip>
{/** 下划线 */}
{/** underline */}
<Tooltip
content={I18n.t('workflow_note_underline')}
mouseEnterDelay={tooltipDelay}
@@ -230,7 +230,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
</span>
</Tooltip>
{/** 删除线 */}
{/** Strikethrough */}
<Tooltip
content={I18n.t('workflow_note_strikethrough')}
mouseEnterDelay={tooltipDelay}
@@ -251,7 +251,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
<div className={separatorClass} />
{/** 无序列表 */}
{/** unordered list */}
<Tooltip
content={I18n.t('workflow_note_bulleted_list')}
mouseEnterDelay={tooltipDelay}
@@ -270,7 +270,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
</span>
</Tooltip>
{/** 有序列表 */}
{/** ordered list */}
<Tooltip
content={I18n.t('workflow_note_numbered_list')}
mouseEnterDelay={tooltipDelay}
@@ -289,7 +289,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
</span>
</Tooltip>
{/** 引用 */}
{/** quote */}
<Tooltip
content={I18n.t('workflow_note_quote')}
mouseEnterDelay={tooltipDelay}
@@ -310,7 +310,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
<div className={separatorClass} />
{/** 链接 */}
{/** link */}
<Tooltip
content={I18n.t('workflow_note_hyperlink')}
mouseEnterDelay={tooltipDelay}
@@ -346,7 +346,7 @@ export const CommentToolbar: FC<ICommentToolbar> = props => {
// cp-disable-next-line
placeholder="https://"
style={{
// 这里需要覆盖样式tailwind class优先级不够高
// Overlay style is needed here, tailwind class priority is not high enough
backgroundColor: '#fff',
display: 'flex',
alignItems: 'center',

View File

@@ -21,35 +21,35 @@ export enum CommentEditorFormField {
Note = 'note',
}
/** 编辑器事件 */
/** editor event */
export enum CommentEditorEvent {
/** 内容变更事件 */
/** content change event */
Change = 'change',
/** 多选事件 */
/** multiple choice event */
MultiSelect = 'multiSelect',
/** 单选事件 */
/** radio event */
Select = 'select',
/** 失焦事件 */
/** out of focus event */
Blur = 'blur',
}
/** 编辑器块格式 */
/** editor block format */
export enum CommentEditorBlockFormat {
/** 段落 */
/** paragraph */
Paragraph = 'paragraph',
/** 标题一 */
/** Title I */
HeadingOne = 'heading-one',
/** 标题二 */
/** Title II */
HeadingTwo = 'heading-two',
/** 标题三 */
/** Title III */
HeadingThree = 'heading-three',
/** 引用 */
/** quote */
Blockquote = 'block-quote',
/** 无序列表 */
/** unordered list */
BulletedList = 'bulleted-list',
/** 有序列表 */
/** ordered list */
NumberedList = 'numbered-list',
/** 列表项 */
/** list item */
ListItem = 'list-item',
}
@@ -60,21 +60,21 @@ export const CommentEditorListBlockFormat = [
export const CommentEditorLeafType = 'text';
/** 编辑器叶子节点格式 */
/** Editor leaf node format */
export enum CommentEditorLeafFormat {
/** 粗体 */
/** bold */
Bold = 'bold',
/** 斜体 */
/** Italic */
Italic = 'italic',
/** 下划线 */
/** underline */
Underline = 'underline',
/** 删除线 */
/** Strikethrough */
Strikethrough = 'strikethrough',
/** 链接 */
/** link */
Link = 'link',
}
/** 编辑器默认块 */
/** Editor default block */
export const CommentEditorDefaultBlocks = [
{
type: CommentEditorBlockFormat.Paragraph,
@@ -82,13 +82,13 @@ export const CommentEditorDefaultBlocks = [
},
];
/** 编辑器默认值 */
/** Editor Default */
export const CommentEditorDefaultValue = JSON.stringify(
CommentEditorDefaultBlocks,
);
/** 工具栏显示延迟 */
/** Toolbar display delay */
export const CommentToolbarDisplayDelay = 200;
/** 默认链接 */
/** default link */
export const CommentDefaultLink = 'about:blank';

View File

@@ -64,7 +64,7 @@ export const useModel = () => {
const model = useMemo(createModel, []);
// 同步失焦状态
// Synchronized out-of-focus state
useEffect(() => {
if (focused) {
return;
@@ -72,14 +72,14 @@ export const useModel = () => {
model.setFocus(focused);
}, [focused, model]);
// 同步表单值初始化
// Synchronized form value initialization
useEffect(() => {
const value = formModel.getValueIn<string>(CommentEditorFormField.Note);
model.setValue(value); // 设置初始值
model.selectEnd(); // 设置初始化光标位置
model.setValue(value); // Set initial value
model.selectEnd(); // Set the initialization cursor position
}, [formModel, model]);
// 同步表单外部值变化:undo/redo/协同
// Synchronize form external value changes: undo/redo/synergy
useEffect(() => {
const disposer = formModel.onFormValuesChange(({ name }) => {
if (name !== CommentEditorFormField.Note) {

View File

@@ -39,17 +39,17 @@ export const useOverflow = (params: {
return editorHeight > containerHeight;
}, [model, height, playground]);
// 更新 overflow
// Update overflow
const updateOverflow = useCallback(() => {
setOverflow(isOverflow());
}, [isOverflow]);
// 监听高度变化
// Monitor height change
useEffect(() => {
updateOverflow();
}, [height, updateOverflow]);
// 监听 change 事件
// Monitor change events
useEffect(() => {
const changeDispose = model.on<CommentEditorEvent.Change>(
CommentEditorEvent.Change,

View File

@@ -47,7 +47,7 @@ export const useSize = () => {
const [width, setWidth] = useState(formSize?.width ?? size.width);
const [height, setHeight] = useState(formSize?.height ?? size.height);
// 初始化表单值
// Initialize form value
useEffect(() => {
const initSize = formModel.getValueIn<{ width: number; height: number }>(
CommentEditorFormField.Size,
@@ -60,7 +60,7 @@ export const useSize = () => {
}
}, [formModel, width, height]);
// 同步表单外部值变化:初始化/undo/redo/协同
// Synchronize form external value changes: initialize/undo/redo/coordinate
useEffect(() => {
const disposer = formModel.onFormValuesChange(({ name }) => {
if (name !== CommentEditorFormField.Size) {
@@ -118,7 +118,7 @@ export const useSize = () => {
resizeState.originalHeight + bottom - top,
);
// 如果宽度或高度小于最小值,则不更新偏移量
// If the width or height is less than the minimum, the offset is not updated
const newOffsetX =
(left > 0 || right < 0) && newWidth <= minWidth
? resizeState.offsetX
@@ -136,11 +136,11 @@ export const useSize = () => {
resizeState.offsetX = newOffsetX;
resizeState.offsetY = newOffsetY;
// 更新状态
// update status
setWidth(newWidth);
setHeight(newHeight);
// 更新偏移量
// Update Offset
transform.update({
position: {
x: newPositionX,

View File

@@ -66,12 +66,12 @@ export class CommentEditorModel {
this.innerBlocks = CommentEditorDefaultBlocks as CommentEditorBlock[];
}
/** 获取当前值 */
/** Get the current value */
public get value(): string {
return this.innerValue;
}
/** 外部设置模型值 */
/** External setting model values */
public setValue(newValue?: string): void {
const value = newValue ?? CommentEditorDefaultValue;
if (value === this.innerValue) {
@@ -90,12 +90,12 @@ export class CommentEditorModel {
});
}
/** 获取所有块 */
/** Get all blocks */
public get blocks(): CommentEditorBlock[] {
return this.innerBlocks;
}
/** 获取编辑器 DOM 节点 */
/** Get editor DOM node */
public get element(): HTMLDivElement | null {
try {
return ReactEditor.toDOMNode(this.editor, this.editor) as HTMLDivElement;
@@ -105,20 +105,20 @@ export class CommentEditorModel {
}
}
/** 注册命令 */
/** Register command */
public registerCommand(command: CommentEditorCommand): this {
this.commands.push(command);
return this;
}
/** 键盘事件 */
/** keyboard event */
public keydown(
event: Parameters<KeyboardEventHandler<HTMLDivElement>>[0],
): void {
const { ctrlKey, metaKey, shiftKey, key } = event;
// 使用 ctrlKey metaKey 作为统一的修饰键检查
// Use ctrlKey or metaKey as a unified modifier key check
const modifierKey = ctrlKey || metaKey;
// 遍历所有注册的命令
// Iterate through all registered commands
const matchedCommands = this.commands
.filter(command => command.key === key)
.filter(
@@ -137,7 +137,7 @@ export class CommentEditorModel {
});
}
/** 粘贴事件 */
/** paste event */
public paste(event: ClipboardEvent<HTMLDivElement>): void {
const fragment = event.clipboardData.getData(
'application/x-slate-fragment',
@@ -151,12 +151,12 @@ export class CommentEditorModel {
return;
}
if (this.isBlockMarked(CommentEditorBlockFormat.ListItem)) {
// 清除列表格式,防止粘贴场景下列表嵌套
// Clear list format to prevent nesting of lists under paste scenes
this.markBlock(CommentEditorBlockFormat.Paragraph);
}
}
/** 注册事件 */
/** Register an event */
public on<T extends CommentEditorEvent>(
event: T,
callback: (params: CommentEditorEventParams<T>) => void,
@@ -167,7 +167,7 @@ export class CommentEditorModel {
};
}
/** 编辑器聚焦/失焦 */
/** Editor Focus/Out of Focus */
public setFocus(focused: boolean): void {
if (focused && !this.focused) {
ReactEditor.focus(this.editor);
@@ -178,9 +178,9 @@ export class CommentEditorModel {
}
}
/** 选择末尾 */
/** Select end */
public selectEnd(): void {
// 获取编辑器中的所有节点
// Get all nodes in the editor
const nodes = Array.from(
Editor.nodes(this.editor, {
at: [],
@@ -188,34 +188,34 @@ export class CommentEditorModel {
}),
);
// 如果没有节点,直接返回
// If there is no node, return directly
if (nodes.length === 0) {
return;
}
// 获取最后一个块级节点的路径
// Get the path to the last block-level node
const lastNodeEntry = nodes[nodes.length - 1];
const lastPath = lastNodeEntry[1];
// 获取最后一个节点的末尾点
// Get the end point of the last node
const endPoint = Editor.end(this.editor, lastPath);
// 创建一个新的范围,起点和终点都是最后一个节点的末尾
// Create a new range, starting and ending at the end of the last node
const range: Range = {
anchor: endPoint,
focus: endPoint,
};
// 将选择设置为新创建的范围
// Set the selection to the newly created range
Transforms.select(this.editor, range);
}
/** 获取聚焦状态 */
/** Get focus status */
public get focused(): boolean {
return ReactEditor.isFocused(this.editor);
}
/** 数据变更事件 */
/** data change event */
public async fireChange(): Promise<void> {
const isAstChange = this.editor.operations.some(
op => 'set_selection' !== op.type,
@@ -233,11 +233,11 @@ export class CommentEditorModel {
}
}
/** 标记块 */
/** tag block */
public markBlock(format: CommentEditorBlockFormat): void {
const isMarked = this.isBlockMarked(format);
const isListBlock = CommentEditorListBlockFormat.includes(format);
// 清空父级存在的块
// Empty parent existing blocks
Transforms.unwrapNodes<CommentEditorNode>(this.editor, {
match: n =>
!Editor.isEditor(n) &&
@@ -249,11 +249,11 @@ export class CommentEditorModel {
});
const getBlockType = () => {
if (isMarked) {
// 重置为段落
// Reset to paragraph
return CommentEditorBlockFormat.Paragraph;
}
if (isListBlock) {
// 列表块重置为列表项
// List block is reset to list item
return CommentEditorBlockFormat.ListItem;
}
return format;
@@ -278,7 +278,7 @@ export class CommentEditorModel {
}
}
/** 块是否标记 */
/** Is the block marked? */
public isBlockMarked(format: CommentEditorBlockFormat): boolean {
const { selection } = this.editor;
if (!selection) {
@@ -297,7 +297,7 @@ export class CommentEditorModel {
return !!match;
}
/** 标记叶子 */
/** Mark leaves */
public markLeaf(
format: CommentEditorLeafFormat,
value: boolean | string = true,
@@ -310,13 +310,13 @@ export class CommentEditorModel {
}
}
/** 叶子是否标记 */
/** Are the leaves marked? */
public isLeafMarked(format: CommentEditorFormat): boolean {
const marks = Editor.marks(this.editor);
return !!marks && !!marks[format];
}
/** 获取叶子值 */
/** Get leaf value */
public getLeafValue(
format: CommentEditorFormat,
): boolean | string | undefined {
@@ -324,7 +324,7 @@ export class CommentEditorModel {
return marks?.[format];
}
/** 设置叶子值 */
/** Set leaf value */
public setLeafValue(
format: CommentEditorFormat,
value: boolean | string = true,
@@ -332,7 +332,7 @@ export class CommentEditorModel {
Editor.addMark(this.editor, format, value);
}
/** 清除当前块的所有格式 */
/** Clear all formatting of the current block */
public clearFormat(): void {
Object.values(CommentEditorLeafFormat).forEach(format => {
Editor.removeMark(this.editor, format);
@@ -340,7 +340,7 @@ export class CommentEditorModel {
this.markBlock(CommentEditorBlockFormat.Paragraph);
}
/** 获取块文本 */
/** Get block text */
public getBlockText(): {
text: string;
before: string;
@@ -349,53 +349,53 @@ export class CommentEditorModel {
const { selection } = this.editor;
const emptyResult = { text: '', before: '', after: '' };
// 如果不存在光标,返回空结果
// If no cursor exists, return an empty result
if (!selection?.anchor) {
return emptyResult;
}
// 获取当前块级元素
// Get the current block-level element
const entry = Editor.above(this.editor, {
match: (n): boolean =>
SlateElement.isElement(n) && Editor.isBlock(this.editor, n),
});
// 如果没有找到块级元素,返回空结果
// If no block-level elements are found, an empty result is returned
if (!entry) {
return emptyResult;
}
const [block, path] = entry;
// 确保 block Element 类型
// Make sure the block is an Element type
if (!SlateElement.isElement(block)) {
return emptyResult;
}
// 获取完整文本
// Get the full text
const text = SlateNode.string(block);
// 创建一个范围从块的开始到当前光标位置
// Create a range from the beginning of the block to the current cursor position
const beforeRange = {
anchor: Editor.start(this.editor, path),
focus: selection.anchor,
};
// 获取光标前的文本
// Get the text in front of the cursor
const before = Editor.string(this.editor, beforeRange);
// 计算光标后的文本
// Calculate the text after the cursor
const after = text.slice(before.length);
return { text, before, after };
}
/** 创建编辑器 */
/** Create editor */
private createEditor(): ReactEditor {
return this.withInsertBreak(withReact(withHistory(createEditor())));
}
/** 是否初始化 */
/** Whether to initialize */
private get initialized(): boolean {
return (
Array.isArray(this.editor.children) && this.editor.children.length > 0
@@ -403,43 +403,43 @@ export class CommentEditorModel {
}
/**
* 同步编辑器实例内容
* > **NOTICE:** *为确保不影响性能,应仅在外部值变更导致编辑器值与模型值不一致时调用*
* Synchronize editor instance content
* > ** NOTICE: ** * To ensure that performance is not affected, it should only be called when an external value change causes the editor value to be inconsistent with the model value.
*/
private syncEditorValue(): void {
if (!this.initialized) {
// 未初始化时 Slate DOM 未创建,无需主动同步,否则 Slate 会报错找不到 DOM
// Slate DOM is not created when not initialized, no active synchronization is required, otherwise Slate will report an error and cannot find the DOM.
return;
}
try {
Editor.withoutNormalizing(this.editor, () => {
// 删除所有现有内容
// Delete all existing content
Transforms.delete(this.editor, {
at: {
anchor: Editor.start(this.editor, []),
focus: Editor.end(this.editor, []),
},
});
// 确保选区在文档开始
// Make sure the selection is at the beginning of the document
Transforms.select(this.editor, Editor.start(this.editor, []));
// 插入新的空白文档
// 直接设置编辑器的 children
// Insert a new blank document
// Set the children of the editor directly
this.editor.children = this.blocks as unknown as Descendant[];
});
} catch (error) {
// Slate 内部可能存在 DOM 处理错误,不会影响外部使用
// There may be DOM handling errors inside Slate, which will not affect external use
console.error('@CommentEditorModel::SyncEditorValue::Error', error);
}
}
/** 插入换行 */
/** Insert newline */
private withInsertBreak(editor: ReactEditor): ReactEditor {
const { insertBreak } = editor;
editor.insertBreak = () => {
const { selection } = editor;
// 如果没有选择或选择不是折叠的,执行默认的换行操作
// If there is no selection or the selection is not collapsed, perform the default line wrapping operation
if (!selection || !Range.isCollapsed(selection)) {
insertBreak();
return;
@@ -451,7 +451,7 @@ export class CommentEditorModel {
const { after } = this.getBlockText();
// 如果没有找到块或者不在行尾,执行默认的换行操作
// If no block is found or not at the end of the line, perform the default line break
if (!result || after !== '') {
insertBreak();
return;
@@ -460,13 +460,13 @@ export class CommentEditorModel {
const [node, path] = result;
const blockType = (node as unknown as CommentEditorBlock).type;
// 执行原本的换行操作
// Execute the original line feed
insertBreak();
// 获取新插入的块的路径
// Get the path to the newly inserted block
const newPath = SlatePath.next(path);
// 只有在非列表和非引用的情况下才清除格式
// Clear formatting only if it is not a list or a reference
const shouldClearFormat = ![
CommentEditorBlockFormat.NumberedList,
CommentEditorBlockFormat.BulletedList,
@@ -478,23 +478,23 @@ export class CommentEditorModel {
this.clearFormatAtPath(newPath);
}
// 确保光标在新行的开始
// Make sure the cursor is at the beginning of a new line
Transforms.select(editor, Editor.start(editor, newPath));
};
return editor;
}
/** 清除指定路径的块级格式和内联格式 */
/** Clears the block-level and internal connection formats of the specified path */
private clearFormatAtPath(path: SlatePath): void {
// 选中指定路径的整个块
// Select the entire block of the specified path
Transforms.select(this.editor, path);
// 使用现有的 clearFormat 方法清除格式
// Use the existing clearFormat method to clear the format
this.clearFormat();
}
/** 数据变更事件 */
/** data change event */
private change(): void {
this.innerBlocks = this.editor.children as unknown as CommentEditorBlock[];
const value = this.serialize(this.innerBlocks);
@@ -508,7 +508,7 @@ export class CommentEditorModel {
});
}
/** 单选事件 */
/** radio event */
private select(): void {
const { selection } = this.editor;
if (!selection) {
@@ -521,7 +521,7 @@ export class CommentEditorModel {
this.emitter.emit(CommentEditorEvent.Select, {});
}
/** 多选事件 */
/** multiple choice event */
private multiSelect(): void {
const { selection } = this.editor;
if (!selection) {
@@ -534,12 +534,12 @@ export class CommentEditorModel {
this.emitter.emit(CommentEditorEvent.MultiSelect, {});
}
/** 序列化 */
/** Serialization */
private serialize(blocks: CommentEditorBlock[]): string | undefined {
return CommentEditorParser.toJSON(blocks);
}
/** 反序列化 */
/** deserialization */
private deserialize(value?: string): CommentEditorBlock[] | undefined {
return CommentEditorParser.fromJSON(value);
}

View File

@@ -21,7 +21,7 @@ import type { CommentEditorBlock, CommentEditorLeaf } from '../type';
import { CommentEditorBlockFormat, CommentEditorLeafFormat } from '../constant';
export namespace CommentEditorHTMLParser {
// 转换叶子节点为 HTML
// Convert leaf nodes to HTML
const convertLeafToHtml = (leaf: CommentEditorLeaf): string => {
let result: string = leaf.text;
@@ -44,7 +44,7 @@ export namespace CommentEditorHTMLParser {
return result;
};
// 转换段落为 HTML
// Convert paragraphs to HTML
const convertParagraphToHtml = (block: CommentEditorBlock): string => {
const content: string = block.children
.map(child => {
@@ -57,7 +57,7 @@ export namespace CommentEditorHTMLParser {
return `<p>${content}</p>`;
};
// 转换标题为 HTML
// Convert title to HTML
const convertHeadingToHtml = (block: CommentEditorBlock): string => {
const content: string = block.children
.map(child => {
@@ -76,9 +76,9 @@ export namespace CommentEditorHTMLParser {
return `<h${level}>${content}</h${level}>`;
};
// 转换引用为 HTML
// Convert reference to HTML
const convertBlockquoteToHtml = (block: CommentEditorBlock): string => {
// 处理引用块中的内容
// Process the content in the reference block
const processQuoteContent = (
child: CommentEditorLeaf | CommentEditorBlock,
): string => {
@@ -88,14 +88,14 @@ export namespace CommentEditorHTMLParser {
return convertBlockToHtml(child);
};
// 处理所有子元素
// Handle all child elements
const content: string = block.children.map(processQuoteContent).join('');
// 将内容包装在 <p> 标签内,然后放入 <blockquote> 标签
// Wrap the content inside a < p > tag, then put in a < blockquote > tag
return `<blockquote><p>${content}</p></blockquote>`;
};
// 转换列表为 HTML
// Convert list to HTML
const convertListToHtml = (block: CommentEditorBlock): string => {
const isNumbered: boolean =
block.type === CommentEditorBlockFormat.NumberedList;
@@ -131,7 +131,7 @@ export namespace CommentEditorHTMLParser {
return `<${listTag}>${content}</${listTag}>`;
};
// 转换块为 HTML
// Convert block to HTML
const convertBlockToHtml = (block: CommentEditorBlock): string => {
switch (block.type) {
case CommentEditorBlockFormat.Paragraph:
@@ -150,7 +150,7 @@ export namespace CommentEditorHTMLParser {
}
};
// 主函数:将整个 schema 转换为 HTML
// Main function: Converts the entire schema to HTML
export const to = (schema: CommentEditorBlock[]): string => {
const html: string = schema
.map(block => convertBlockToHtml(block))

View File

@@ -23,7 +23,7 @@ import {
} from '../constant';
export namespace CommentEditorJSONParser {
// 处理单个节点
// Processing a single node
const processNode = (node: CommentEditorBlock): CommentEditorBlock => {
if ('text' in node && !node.type) {
return {
@@ -42,11 +42,11 @@ export namespace CommentEditorJSONParser {
return node as CommentEditorBlock;
};
// 主函数:处理整个 schema
// Main function: handle the entire schema
const addLeafType = (schema: CommentEditorBlock[]): CommentEditorBlock[] =>
schema.map(processNode);
/** JSON 转换为 Schema */
/** Convert JSON to Schema */
export const from = (value?: string): CommentEditorBlock[] | undefined => {
if (!value || value === '') {
return CommentEditorDefaultBlocks as CommentEditorBlock[];
@@ -60,7 +60,7 @@ export namespace CommentEditorJSONParser {
}
};
/** schema 转换为 JSON */
/** Schema to JSON */
export const to = (schema: CommentEditorBlock[]): string | undefined => {
try {
const value = JSON.stringify(addLeafType(schema));

View File

@@ -20,7 +20,7 @@ import type { CommentEditorBlock, CommentEditorLeaf } from '../type';
import { CommentEditorBlockFormat, CommentEditorLeafFormat } from '../constant';
export namespace CommentEditorMarkdownParser {
// 转换叶子节点为 Markdown
// Convert leaf nodes to Markdown
const convertLeafToMarkdown = (leaf: CommentEditorLeaf): string => {
let result: string = leaf.text;
@@ -43,7 +43,7 @@ export namespace CommentEditorMarkdownParser {
return result;
};
// 转换段落为 Markdown
// Convert paragraphs to Markdown
const convertParagraphToMarkdown = (block: CommentEditorBlock): string => {
const content: string = block.children
.map(child => {
@@ -56,7 +56,7 @@ export namespace CommentEditorMarkdownParser {
return `${content}\n\n`;
};
// 转换标题为 Markdown
// Convert headings to Markdown
const convertHeadingToMarkdown = (block: CommentEditorBlock): string => {
const content: string = block.children
.map(child => {
@@ -75,9 +75,9 @@ export namespace CommentEditorMarkdownParser {
return `${'#'.repeat(level)} ${content}\n\n`;
};
// 转换引用为 Markdown
// Convert Reference to Markdown
const convertBlockquoteToMarkdown = (block: CommentEditorBlock): string => {
// 处理引用块中的内容
// Process the content in the reference block
const processQuoteContent = (
child: CommentEditorLeaf | CommentEditorBlock,
): string => {
@@ -90,24 +90,24 @@ export namespace CommentEditorMarkdownParser {
return convertBlockToMarkdown(child);
};
// 将内容转换为引用格式
// Convert content to citation format
const convertToQuoteFormat = (content: string): string =>
content
.split('\n')
.map((line: string): string => `> ${line}`)
.join('\n');
// 处理所有子元素
// Handle all child elements
const content: string = block.children
.map(processQuoteContent)
.join('\n')
.trim();
// 转换为引用格式并添加额外的换行符
// Convert to reference format and add extra line breaks
return `${convertToQuoteFormat(content)}\n\n`;
};
// 转换列表为 Markdown
// Convert List to Markdown
const convertListToMarkdown = (
block: CommentEditorBlock,
indent = '',
@@ -148,7 +148,7 @@ export namespace CommentEditorMarkdownParser {
return `${content}\n`;
};
// 转换块为 Markdown
// Convert block to Markdown
const convertBlockToMarkdown = (block: CommentEditorBlock): string => {
switch (block.type) {
case CommentEditorBlockFormat.Paragraph:
@@ -167,7 +167,7 @@ export namespace CommentEditorMarkdownParser {
}
};
// 主函数:将整个 schema 转换为 Markdown
// Main function: Converts the entire schema to Markdown
export const to = (schema: CommentEditorBlock[]): string => {
const markdown: string = schema
.map(block => convertBlockToMarkdown(block))

View File

@@ -19,10 +19,10 @@ import type { CommentEditorBlock, CommentEditorLeaf } from '../type';
import { CommentEditorBlockFormat } from '../constant';
export namespace CommentEditorTextParser {
// 将叶子节点转换为纯文本
// Convert leaf nodes to plain text
const convertLeafToText = (leaf: CommentEditorLeaf): string => leaf.text;
// 将段落转换为纯文本
// Convert paragraphs to plain text
const convertParagraphToText = (block: CommentEditorBlock): string => {
const content: string = block.children
.map(child => {
@@ -35,7 +35,7 @@ export namespace CommentEditorTextParser {
return `${content}\n`;
};
// 将标题转换为纯文本
// Convert the title to plain text
const convertHeadingToText = (block: CommentEditorBlock): string => {
const content: string = block.children
.map(child => {
@@ -48,7 +48,7 @@ export namespace CommentEditorTextParser {
return `${content}\n`;
};
// 将引用转换为纯文本
// Convert a reference to plain text
const convertBlockquoteToText = (block: CommentEditorBlock): string => {
const processQuoteContent = (
child: CommentEditorLeaf | CommentEditorBlock,
@@ -67,7 +67,7 @@ export namespace CommentEditorTextParser {
return `${content}\n`;
};
// 将列表转换为纯文本
// Convert a list to plain text
const convertListToText = (
block: CommentEditorBlock,
indent = '',
@@ -102,7 +102,7 @@ export namespace CommentEditorTextParser {
return content;
};
// 将块转换为纯文本
// Convert a block to plain text
const convertBlockToText = (block: CommentEditorBlock): string => {
switch (block.type) {
case CommentEditorBlockFormat.Paragraph:
@@ -121,7 +121,7 @@ export namespace CommentEditorTextParser {
}
};
// 主函数:将整个 schema 转换为纯文本
// Main function: Converts the entire schema to plain text
export const to = (schema: CommentEditorBlock[]): string => {
const text: string = schema
.map(block => convertBlockToText(block))

View File

@@ -26,11 +26,11 @@ import styles from './condition-item-logic.module.less';
export interface ConditionItemLogicProps {
/**
* 逻辑 And Or
* And Logic Or
*/
logic?: ConditionLogic;
/**
* 逻辑 And Or change 回调
* And Or change the logic
*/
onChange: (logic: ConditionLogic) => void;
showStroke?: boolean;

View File

@@ -106,7 +106,7 @@ export const Conversations: React.FC<ConversationsProps> = ({
globalState.projectId ||
'';
// 接口得到的总数并非真实的总数,前端可能会拼接 options
// The total number obtained by the interface is not the real total, and the front end may splice options.
const listMaxHeight = useMemo(() => {
const realTotal = (staticList?.length || 0) + (dynamicList?.length || 0);
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
@@ -123,14 +123,14 @@ export const Conversations: React.FC<ConversationsProps> = ({
const initFn = async () => {
const list = await fetchList();
// 从开始节点拿数据设置初始值
// Get the data from the start node to set the initial value
if (startNode && !value) {
const outputs = getStartNodeOutputs();
const defaultName =
outputs.find(output => output.name === CONVERSATION_NAME)
?.defaultValue || '';
const findItem = list.find(item => item.label === defaultName);
// 默认选中开始节点的 conversation_name,如果没有,默认选中 Default 默认会话
// The conversation_name of the start node is selected by default, if not, Default default session is selected by default
handleChange(findItem?.value || '0', findItem);
}
};
@@ -142,8 +142,8 @@ export const Conversations: React.FC<ConversationsProps> = ({
}, [projectId]);
useEffect(() => {
// 加锁,确保当前列表内容和传入的 projectId 一致,然后从列表中搜索对应的项
// 初始化任何一个 list 没有 ready 不走后续逻辑
// Lock to ensure that the current list content is consistent with the incoming projectId, and then search for the corresponding item from the list
// Initialize any list without getting ready for follow-up logic
if (fetchingProjectId !== projectId || !staticList || !dynamicList) {
return;
}

View File

@@ -24,8 +24,8 @@ import { UIIconButton } from '@coze-arch/bot-semi';
const DELAY = 4000;
/**
* 复制按钮,点击后切换到成功状态
* 默认延迟 4 秒
* Copy button, click and switch to success state
* Default delay of 4 seconds
*/
export const CopyButton = ({
value = '',

View File

@@ -20,8 +20,8 @@ import { type CSSProperties } from 'react';
import { useNodeRenderData } from '../../hooks';
/**
* 自定义端口组件, 支持展开/收起;
* 节点收起时 端口 dom 代理到 node-render这一层避免被display:none影响。
* Custom port components, support expand/retract;
* When a node is stowed, the port dom is proxied to the node-render layer to avoid being affected by display: none.
*/
export const CustomPort = ({
portId,

View File

@@ -25,7 +25,7 @@ import { useWorkflowDetailModalStore } from './use-workflow-detail-modal-store';
import styles from './database-detail-model.module.less';
/**
* 数据库详情弹窗
* database details pop-up
*/
export function DatabaseDetailModal() {
const {
@@ -44,7 +44,7 @@ export function DatabaseDetailModal() {
}
const addRemoveButtonText = isAddedInWorkflow
? // 这个key命名错了 应该是remove from workflow 产品已经录入了 这里还是继续用错误的key
? // This key is named incorrectly. It should be removed from the workflow product. The product has been entered. Continue to use the wrong key here.
I18n.t('workflow_remove_to_workflow')
: I18n.t('workflow_add_to_workflow');

View File

@@ -59,7 +59,7 @@ export const DragTooltip = () => {
return false;
}
if (!message) {
// 仅在有消息时展示
// Show only when there is news
return false;
}
return true;
@@ -68,7 +68,7 @@ export const DragTooltip = () => {
const tooltipRef = useRef<HTMLDivElement>(null);
const offset = useMemo(() => {
const isInProject = Boolean(globalState.projectId);
// 初始位置为画布左上角currentOffset 为距离屏幕左上角的偏移,需要减去画布距离屏幕左上角偏移
// The initial position is the upper left corner of the canvas, and currentOffset is the offset from the upper left corner of the screen. You need to subtract the offset of the canvas from the upper left corner of the screen.
const playgroundOffsetX = isInProject ? 276 : 0;
const playgroundOffsetY = isInProject ? 100 : 73;

View File

@@ -39,16 +39,16 @@ interface ExpandEditorContainerProps {
}
/**
* 弹窗编辑器容器
* @param editorTitle 弹窗标题
* @param editorTooltip 弹窗说明
* @param actions 标题工具栏
* @param closeButton 自定义关闭按钮
* @param closeIconClassName 关闭按钮样式
* @param editorContent 弹窗编辑器区域
* @param containerClassName 容器样式
* @param headerClassName 标题样式
* @param contentClassName 编辑器区域样式
* pop-up editor container
* @param editorTitle popup title
* @param editorTooltip pop-up description
* @param actions title toolbar
* @param closeButton Custom close button
* @param closeIconClassName Close button style
* @param editorContent pop-up editor area
* @param containerClassName Container style
* @param headerClassName title style
* @param contentClassName editor area style
*/
export const ExpandEditorContainer: FC<ExpandEditorContainerProps> = props => {
const {

View File

@@ -34,14 +34,14 @@ interface InnerContainerProps {
}
/**
* 表单内编辑器容器,对 foucs error 状态下边框样式进行封装
* The editor container in the form encapsulates the border style in the foucs error state
* @param name
* @param onBlur
* @param onFocus
* @param isError
* @param onMouseEnter
* @param onMouseLeave
* @param className 编辑器外层样式
* @param className editor outer style
* @param children
*/
export const InnerEditorContainer: FC<InnerContainerProps> = props => {
@@ -61,7 +61,7 @@ export const InnerEditorContainer: FC<InnerContainerProps> = props => {
const { getNodeSetterId } = useNodeTestId();
const dataTestID = getNodeSetterId(name);
// 设置防抖防止 onFocus / onBlur 在点击时出现抖动
// Set anti-shake to prevent onFocus/onBlur from shaking when clicked
const setFocus = useCallback(
debounce((newFocusValue: boolean) => {
_setFocus(newFocusValue);

View File

@@ -15,7 +15,7 @@
*/
/**
* 覆盖在整个画布上的布局浮层,承载一些有联动关系的悬浮组件
* Layout floating layer covering the entire canvas, carrying some linked suspension components
*/
import React, { useEffect, useLayoutEffect, useRef } from 'react';
@@ -47,7 +47,7 @@ export const FloatLayout: React.FC<
const size = useSize(ref);
useLayoutEffect(() => {
// 只触发一次
// Only trigger once
floatLayoutService.register(components);
}, []);
@@ -73,7 +73,7 @@ export const FloatLayout: React.FC<
templateState.openTemplate();
} else {
templateState.closeTemplate();
// 流程模版关闭动画为 200 ms , 待动画结束后关闭 bottom 面板
// Process template closing animation for 200 ms, close bottom panel after animation
setTimeout(() => {
floatLayoutService.close('bottom');
}, 300);
@@ -83,14 +83,14 @@ export const FloatLayout: React.FC<
return (
<div className={styles['float-layout']} ref={ref}>
<div className={styles['left-panel']}>
{/* 主面板 */}
{/* main panel */}
<div className={styles['left-main-panel']}>{children}</div>
{/* 底部面板,优先级比主区域高,默认高度为 0一旦有高度会挤压主面板 */}
{/* Bottom panel, the priority is higher than the main area, the default height is 0, once there is a height, it will squeeze the main panel */}
<div className={styles['left-bottom-panel']}>
<FloatPanel panel={floatLayoutService.bottom} />
</div>
</div>
{/* 右侧区域,优先级最高,默认宽度为 0一旦有宽度就会挤压左侧 */}
{/* The right area has the highest priority. The default width is 0. Once there is a width, the left side will be squeezed. */}
<div className={styles['right-panel']}>
<FloatPanel panel={floatLayoutService.right} />
</div>

View File

@@ -30,16 +30,16 @@ export const FloatPanel: React.FC<FloatPanelProps> = ({ panel }) => {
useEffect(() => {
const dispose = panel.onUpdate(next => {
/**
* 点击空白区域关闭 SideSheet 的场景下
* Click on the blank area to close the scene of SideSheet.
*
* 问题:
* - 直接关闭 SideSheet 会导致抽屉内表单的 Blur 事件没有触发UI 就先销毁了
* Question:
* - Closing SideSheet directly will cause the Blur event of the form in the drawer to not be triggered, and the UI will be destroyed first
*
* 解决思路:
* - UI 更新的优先级需要低于抽屉内表单 Blur 相关的数据更新
* Solution ideas:
* - UI updates need to be prioritized lower than form blur related data updates in the drawer
*
* 具体方案:
* - 使用 startTransition 将这次 UI 销毁的优先级降低执行,让 Blur 相关的数据更新可以先于抽屉的 UI 销毁前执行
* Specific plan:
* - Use start Transition to lower the priority of UI destruction this time, so that Blur-related data updates can be executed before the UI destruction of the drawer
*/
startTransition(() => {
nodeRef.current = next;

View File

@@ -49,7 +49,7 @@ export const RoleButton = () => {
const ref = useRef(null);
/**
* 是否是初次打开角色配置面板
* Is this the first time to open the role configuration panel?
*/
const isInitRef = useRef(false);
@@ -68,18 +68,18 @@ export const RoleButton = () => {
});
/**
* 初始化请求角色数据
* Initialize request role data
*/
useEffect(() => {
roleService.load();
}, [roleService]);
/**
* 是否自动打开配置面板
* Whether to automatically open the configuration panel
*/
useEffect(() => {
/**
* 未经修改的流程 & 未配置过角色配置 => 自动展开角色面板
* Unmodified Process & Unconfigured Role Configuration = > Automatically Expand Role Panel
*/
if (isInitWorkflow && isReady && data === null) {
floatLayoutService.open(LayoutPanelKey.RoleConfig);
@@ -88,7 +88,7 @@ export const RoleButton = () => {
}, [isReady, isInitWorkflow, data, floatLayoutService]);
/**
* 关闭面板时是否打开 onboarding
* Whether onboarding is turned on when closing the panel
*/
useEffect(() => {
const disposable = floatLayoutService.onUnmount(handlePanelClose);

View File

@@ -55,7 +55,7 @@ interface AutoGenerateProps {
};
showAiAvatar: boolean;
/**
* 最多允许多少个候选
* How many candidates are allowed at most?
* @default 5
*/
maxCandidateCount?: number;
@@ -66,7 +66,7 @@ interface PictureItem {
uid: string;
}
// 自动生成头像错误码
// Automatically generate avatar error codes
enum ErrorCode {
OVER_QUOTA_PER_DAY = 700012034,
CONTENT_NOT_LEGAL = 700012050,
@@ -176,7 +176,7 @@ export const AutoGenerate = (props: AutoGenerateProps) => {
});
const codeNumber = Number((error as { code: number })?.code);
if (codeNumber === ErrorCode.OVER_QUOTA_PER_DAY) {
// 超过单日次数上限
// Exceeding the maximum number of times a day
setTotalCount(MAX_TOTAL_COUNT);
Toast.error({
content: I18n.t('bot_edit_profile_pircture_autogen_quota_tooltip'),
@@ -212,7 +212,7 @@ export const AutoGenerate = (props: AutoGenerateProps) => {
};
useEffect(() => {
// 获取当日总生成次数
// Get the total number of spawns for the day
DeveloperApi.GetGenerateIconInfo()
.then(({ data }) => {
setTotalCount(Number(data?.current_day_count));
@@ -272,7 +272,7 @@ export const AutoGenerate = (props: AutoGenerateProps) => {
[s['loading-hover']]: loadingHover && !picture.url,
})}
>
{/* 二次hover展示取消 */}
{/* Secondary hover display cancelled */}
{hoverCount.current > 1 && loadingHover && !picture.url ? (
<div
className={s.mask}
@@ -285,7 +285,7 @@ export const AutoGenerate = (props: AutoGenerateProps) => {
</div>
) : null}
{/* 选中图片蒙版 */}
{/* Select image mask */}
{checkedId === idx && (
<div className={s.mask}>
<IconCozCheckMark className="text-[16]" />

View File

@@ -44,7 +44,7 @@ interface PackageUploadProps {
contentNotLegalText?: string;
};
/**
* 自动生成的最大候选数量
* Maximum number of candidates automatically generated
* @default 5
*/
maxCandidateCount?: number;
@@ -56,7 +56,7 @@ interface PackageUploadProps {
}
export const RoleAvatarUpload = (props: PackageUploadProps) => {
// 业务
// business
const {
onChange,
value,
@@ -108,7 +108,7 @@ export const RoleAvatarUpload = (props: PackageUploadProps) => {
maxSize={2 * 1024}
onSizeError={() => {
Toast.error({
// starling 切换
// Starling toggle
content: I18n.t(
'dataset_upload_image_warning',
{},

View File

@@ -54,11 +54,11 @@ function customUploadRequest(
try {
const { fileInstance } = file;
// 业务
// business
if (fileInstance) {
const extension = getFileExtension(file.name);
// 业务
// business
(async () => {
try {
const base64 = await getBase64(fileInstance);
@@ -72,7 +72,7 @@ function customUploadRequest(
onSuccess?.(result.data);
afterUploadCustom?.();
} catch (error) {
// 如参数校验失败情况会走到catch
// If parameter validation fails, it will go to catch.
afterUploadCustom?.();
}
})();

View File

@@ -107,7 +107,7 @@ export const CropperCover: React.FC<CropperCoverProps> = ({
<Tag
color="primary"
prefixIcon={null}
// 这里适配背景图色 颜色固定不改变
// The background cover color is suitable here, and the color is fixed and does not change.
className="!text-white !bg-[rgba(0,0,0,0.28)]"
>
{mode === 'pc'

View File

@@ -94,16 +94,16 @@ export const CropperImg: React.FC<CropperProps> = ({
initialAspectRatio={size.width / size.height}
src={url}
style={{ height: size.height, width: size.width }}
background={false} // 是否在容器内显示网格背景
background={false} // Whether to display a grid background within the container
guides={false}
zoom={debouncedZoom}
ref={cropperRef}
dragMode="move" // 图片容器可移动
viewMode={0} // 定义cropper的视图模式0允许裁剪框可以延伸到图片容器之外
modal={false} // 是否在图片和裁剪框之间显示黑色蒙版
dragMode="move" // Image container removable
viewMode={0} // Define the view mode of the cropper, 0 allows the cropping box to extend beyond the image container
modal={false} // Whether to display a black mask between the image and the crop box
center={false}
cropBoxMovable={false} // 是否可以拖拽裁剪框 默认true
cropBoxResizable={false} // 默认true ,是否允许拖动 改变裁剪框大小
cropBoxMovable={false} // Whether you can drag and drop the crop box, the default is true.
cropBoxResizable={false} // Default true, whether to allow dragging, change the size of the crop box
highlight={false}
autoCropArea={1}
minCanvasHeight={size.height}

View File

@@ -56,7 +56,7 @@ export const useCropperImg = ({
handleGradientPosition();
}, [url]);
// 设置最大缩放比例
// Set the maximum zoom ratio
const onZoom = () => {
const {
width = 0,
@@ -78,7 +78,7 @@ export const useCropperImg = ({
});
}
}
// TODO:因没有缩放end事件缩放实时获取主题色大图卡顿严重故此场景临时先不获取主题色修改交互or尝试webworker解决此问题
// TODO: Because there is no zoom end event, the zoom gets the theme color in real time. The large picture card is serious, so the scene does not get the theme color temporarily, modify the interaction or try webworker to solve this problem.
// await handleThemeColor();
};
@@ -95,12 +95,12 @@ export const useCropperImg = ({
}
const canvasData = cropperObj?.getCanvasData();
const imgData = cropperObj?.getImageData();
// 图片下边缘 距离 裁剪区下边缘的偏移距离
// Image lower edge, distance, offset distance of lower edge of crop area
const scaleTop = imgData.height + canvasData.top - size.height;
// 图片左边缘 距离 裁剪区左 边缘的偏移距离
// Image left edge, distance, offset distance from the left edge of the crop area
const scaleLeft = ceil(imgData.left + canvasData?.left, 2);
// 图片上下拖拽不能有超出图片容器外
// Drag the picture up and down, it cannot exceed the picture container.
if (y < 0) {
cropperRef.current?.cropper.setCanvasData({
top: 0,
@@ -112,7 +112,7 @@ export const useCropperImg = ({
});
}
// UX产品需求: 左右拖动不能超过 固定“对话气泡容器” left or right 80%
// UX product requirements: drag left and right cannot exceed, fix "dialogue bubble container" left or right 80%
const maxRightOffset = floor(
(size.width - centerWidth) / 2 + centerWidth * 0.4,
2,
@@ -137,9 +137,9 @@ export const useCropperImg = ({
const handleThemeColor = async () => {
const cropperObj = cropperRef.current?.cropper;
// 大图move卡顿优化方案move停止时获取到主题色前 禁止移动
// Large picture move card optimization scheme: move is prohibited before the theme color is obtained when the move stops
cropperObj?.disable();
// 为了加载快一些,设置图片质量中等
// To load faster, set the picture quality to medium
const corp = cropperObj?.getCroppedCanvas()?.toDataURL('image/webp', 0.7);
if (corp) {
const color = await getImageThemeColor(corp);

View File

@@ -18,7 +18,7 @@ import type React from 'react';
import { type DragEventHandler, useRef, useState } from 'react';
const checkHasFileOnDrag = (e: React.DragEvent<HTMLDivElement>) =>
// 判断的依据直接看 types 的类型解释就好了
// The basis for the judgment is to directly look at the type explanation of types
Boolean(e.dataTransfer?.types.includes('Files'));
export const useDragImage = () => {

View File

@@ -123,7 +123,7 @@ export const useSubmitCroppedImage = ({
) {
upload(currentOriginImage.fileInstance);
} else {
// 回填文件时 不需要存原图
// When backfilling the document, there is no need to save the original image.
handleUploadAllSuccess();
}
} catch (error) {

View File

@@ -31,7 +31,7 @@ import {
const MIN_HEIGHT = 640;
// 图片上传限制宽高的函数如果符合条件返回true否者返回false
// The function that limits the width and height of the image upload, returns true if the conditions are met, false if not
export const checkImageWidthAndHeight = (file: Blob): Promise<boolean> =>
new Promise((resolve, reject) => {
const fileReader = new FileReader();
@@ -75,7 +75,7 @@ export const getPictureValue = (value?: BackgroundImageInfo) => {
export const getModeInfo = (mode: 'pc' | 'mobile') => MODE_CONFIG[mode];
// 计算阴影位置
// Calculate shadow position
export const computePosition = (
mode: 'pc' | 'mobile',
cropperRef: RefObject<ReactCropperElement>,
@@ -92,15 +92,15 @@ export const computePosition = (
const canvasData = cropperObj?.getCanvasData();
const imgData = cropperObj?.getImageData();
// 图片左边缘 距离 裁剪区左 边缘的偏移距离
// Image left edge, distance, offset distance from the left edge of the crop area
const imgToScreenLeft = imgData.left + canvasData?.left;
// 图片距离右侧屏幕的距离, > 0 时,右侧未充满图片
// The distance of the picture from the right screen, > 0, the right side is not full of pictures
const imgToScreenRight = cropperWidth - imgData.width - imgToScreenLeft;
// 左侧渲染的渐变需要的left值: 左侧有空隙时渲染left否则无需
// Left value required for gradual change of left rendering: render left when there is a gap on the left, otherwise no need
const leftPercent = floor(imgToScreenLeft / cropperWidth, 2);
// 右侧渲染的渐变需要的right值
// The right value required for gradual change of right rendering
const rightPercent = floor(imgToScreenRight / cropperWidth, 2);
return {
@@ -136,13 +136,13 @@ export function getImageThemeColor(url: string): Promise<string> {
});
}
// 计算主题色
// Calculate theme color
export const computeThemeColor = (
cropperRefList: RefObject<ReactCropperElement>[],
): Promise<string[]> =>
new Promise((resolve, reject) => {
const promises: Promise<string>[] = [];
// 处理每个canvas元素
// Process each canvas element
cropperRefList.forEach(cropperEle => {
promises.push(
new Promise<string>((resolveColor, rejectColor) => {

View File

@@ -30,7 +30,7 @@ export const RoleNameInput = ({ value, onChange, onBlur, ...props }) => {
const handleBlur = () => {
let nextValue = innerValue;
// 如果用户把角色名称删空了,在失焦之后需要回填原本的值
// If the user deletes the character name, the original value needs to be backfilled after being out of focus
if (!nextValue && value) {
nextValue = value;
}

View File

@@ -32,10 +32,10 @@ const formatContent = (str: string) => {
let questions: string[] = [];
for (const part of parts) {
if (part.startsWith(PROLOGUE_KEY)) {
prologue = part.replace(`${PROLOGUE_KEY}\n`, '').trim(); // 提取开场白并去掉标签
prologue = part.replace(`${PROLOGUE_KEY}\n`, '').trim(); // Extract the opening line and remove the label
} else if (part.startsWith(QUESTION_KEY)) {
const questionLines = part.replace(`${QUESTION_KEY}\n`, '').trim(); // 去掉标签
questions = questionLines.split('\n').map(q => q.trim()); // 去掉编号
const questionLines = part.replace(`${QUESTION_KEY}\n`, '').trim(); // Remove the label
questions = questionLines.split('\n').map(q => q.trim()); // Remove the number
}
}

View File

@@ -104,7 +104,7 @@ export const SuggestionList: React.FC<SuggestionListProps> = ({
}, [value, disabled]);
const handleChange = (next: string[]) => {
// 过滤掉所有空项
// Filter out all empty items
const temp = next.filter(i => Boolean(i));
onChange(temp);
};
@@ -119,7 +119,7 @@ export const SuggestionList: React.FC<SuggestionListProps> = ({
handleChange(next);
};
// 长度为 0只在只读态出现提示文字即可
// The length is 0, it only appears in the read-only state, and the prompt text can be used.
if (!innerValue.length) {
return (
<Typography.Text size="small">

View File

@@ -35,7 +35,7 @@ interface InfiniteScrollData {
export const useVoiceOptions = ({ language, voiceType }: VoiceOptionParams) => {
const { space_id } = useParams<DynamicParams>();
const [searchParams] = useSearchParams();
// 资源库 workflow 详情页的 space_id query string
// The workflow details page space_id in the query string
const spaceId = space_id ?? searchParams.get('space_id') ?? '';
const { data, loading, loadMore, loadingMore } = useInfiniteScroll(

View File

@@ -109,7 +109,7 @@ export const VoiceDataSelect: React.FC<VoiceDataSelectProps> = ({
/>
}
innerBottomSlot={
// Select 选项滚动到底部时,若当前未处于加载状态,则触发加载更多
// When the Select option scrolls to the bottom, if it is not currently loaded, it triggers Load More
<LoadMoreTrigger
onLoadMore={() => {
if (!loading && !loadingMore) {

View File

@@ -27,9 +27,9 @@ export const formValue2Data = (values: any) => {
const temp = values || {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data: Record<string, any> = {
// 角色名称
// role name
name: temp.name,
// 角色描述
// Role Description
description: temp.description,
onboarding_info: {
display_all_suggestions: values.display_all_suggestions ?? false,
@@ -43,7 +43,7 @@ export const formValue2Data = (values: any) => {
},
};
// 角色头像
// avatar
if (Array.isArray(temp.avatar) && temp.avatar.length) {
const nextAvatar = temp.avatar[0];
data.avatar = {
@@ -52,17 +52,17 @@ export const formValue2Data = (values: any) => {
};
}
// 开场白文案
// Introductory copy
if (temp.prologue) {
data.onboarding_info.prologue = temp.prologue.replace(/[\r\n]+$/g, '');
}
// 开场白问题
// Opening question
if (temp.questions) {
data.onboarding_info.suggested_questions = temp.questions;
}
// 用户建议
// user suggestion
if (temp.suggest && isObject(temp.suggest)) {
data.suggest_reply_info = temp.suggest;
}
@@ -71,7 +71,7 @@ export const formValue2Data = (values: any) => {
data.background_image_info = temp.background;
}
// 音色
// timbre
if (temp.voices && isObject(temp.voices)) {
if (temp.voices.config && isObject(temp.voices.config)) {
data.audio_config.voice_config_map = temp.voices.config;
@@ -97,7 +97,7 @@ export const data2FormValue = (data: ChatFlowRole = {}) => {
voices: {},
background: {},
suggest_reply_info: {
// 默认是打开状态
// The default is open.
suggest_reply_mode: SuggestReplyInfoMode.System,
},
display_all_suggestions:

View File

@@ -41,7 +41,7 @@ const useVisible = (params: {
const playground = usePlayground();
const { line, selected = false, color } = params;
if (line.disposed) {
// dispose 后,再去获取 line.to | line.from 会导致错误创建端口
// After dispose, getting line.to | line.from will cause an error to create a port
return false;
}
if (playground.config.readonly) {

View File

@@ -30,7 +30,7 @@ export const LinePopover = (props: Record<string, any>) => {
const { hasError, bezier, position } = line;
const { bbox } = bezier;
// 相对位置
// relative position
const toRelative = (p: IPoint) => ({
x: p.x - bbox.x + PADDING,
y: p.y - bbox.y + PADDING,
@@ -49,7 +49,7 @@ export const LinePopover = (props: Record<string, any>) => {
visible={isHovered && hasError}
{...other}
>
{/* tooltip锚点,需要计算线条的中心位置 */}
{/* Tooltip anchor point, you need to calculate the center position of the line */}
<div
style={{
left,

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
// TODO 为了联调先封装一个业务组件,后面再抽象成通用的request select
// TODO first encapsulates a business component for joint debugging, and then abstracts it into a general request select.
import React, { type FC, useEffect, useMemo, useState } from 'react';
import { GenerationDiversity, RESPONSE_FORMAT_NAME } from '@coze-workflow/base';
@@ -56,24 +56,24 @@ export const ModelSetting: FC<ModelSettingProps> = ({
: cacheData.expand,
);
// Customize 要记住用户最后的操作,模式切换回来时用作默认值
// Customize to remember the last action of the user, and use as the default when the mode switches back
const _defaultValue = {
...defaultValue,
[GenerationDiversity.Customize]:
cacheData[id] ?? defaultValue?.[GenerationDiversity.Customize],
};
// 要记住展开状态workflow 级共享
// To remember the expanded state, workflow level sharing
useEffect(() => {
cacheData.expand = settingExpand;
}, [settingExpand]);
const { doms, generationDiversityGroupTitle } = useMemo(() => {
// 特化一:把 response format 过滤掉,在 output 节点中展示
// Specialization 1: Filter out the response format and display it in the output node
const modelParams =
model?.model_params?.filter(m => m.name !== RESPONSE_FORMAT_NAME) ?? [];
// 先拿到分组
// Get the group first
let groups: ModelParamClass[] = [];
modelParams.forEach(m => {
if (
@@ -84,19 +84,19 @@ export const ModelSetting: FC<ModelSettingProps> = ({
}
});
// 特化二:Generation Diversity title 样式是写死的 。跟后端的约定:Generation Diversity class_id === 1
// Specialization 2: Generation Diversity title The style is written dead. Convention with the backend: Generation Diversity class_id === 1
const generationDiversityGroup = groups.find(d => d.class_id === 1);
const _generationDiversityGroupTitle =
generationDiversityGroup?.label || '';
if (generationDiversityGroup) {
// 如果有 Generation Diversity ,必须在最上面
// If there is Generation Diversity, it must be at the top
groups = [
generationDiversityGroup,
...groups.filter(d => d.class_id !== generationDiversityGroup.class_id),
];
}
// 按分组顺序,逐个渲染 setter
// Render setters one by one in grouping order
const _doms: React.ReactNode[] = [];
const _setCacheData = (v: { [k: string]: unknown }) => {
if (v.generationDiversity === GenerationDiversity.Customize) {
@@ -104,16 +104,16 @@ export const ModelSetting: FC<ModelSettingProps> = ({
}
};
groups.forEach((g, i) => {
// 组与组直接插入分割线,第一组不需要
// Groups are inserted directly into the dividing line, the first group is not required
if (i !== 0) {
_doms.push(<Divider />);
}
// 如果收起 && 属于生成多样性这组,就不渲染
// If the hide & & belongs to the group Generative Diversity, it will not be rendered
if (!settingExpand && generationDiversityGroup?.class_id === g.class_id) {
return;
}
// 生成多样性 title 内置了,不需要额外添加
// Generate diversity title built-in, no need to add additional
if (generationDiversityGroup?.class_id !== g.class_id) {
_doms.push(<SettingLayout title={g.label ?? ''} bolder />);
}
@@ -132,14 +132,14 @@ export const ModelSetting: FC<ModelSettingProps> = ({
type,
} = item;
// api/bot/get_type_list 接口给的是 test_keysave 接口需要的是 testKey。前端不感知response_format 除外)
// The API/bot/get_type_list interface gives test_key, and the save interface requires testKey. The front end is not aware (except response_format)
const key = getCamelNameName(name ?? '');
const _value = getValueByType<string | number | undefined>(
value?.[key],
type,
);
// 如果有 options 属性,则用 Select
// If there is an options attribute, use Select
if (options?.length) {
const _optionsList = options.map(option => ({
...option,
@@ -169,7 +169,7 @@ export const ModelSetting: FC<ModelSettingProps> = ({
}
/>,
);
// 否则,就当数字处理,用 Slider + NumberInput
// Otherwise, treat it as number processing, using Slider + NumberInput
} else {
_doms.push(
<SettingSlider
@@ -213,7 +213,7 @@ export const ModelSetting: FC<ModelSettingProps> = ({
// e.stopPropagation();
// e.preventDefault();
// }}
// 阻止画布框选
// Block canvas framing
onMouseDown={e => {
e.stopPropagation();
}}
@@ -293,7 +293,7 @@ export const ModelSetting: FC<ModelSettingProps> = ({
}
/>
{/* 根据后端返回数据动态渲染的 setter */}
{/* A setter dynamically rendered according to the backend */}
{doms}
</div>
);

View File

@@ -30,7 +30,7 @@ export interface OptionItemProps {
name: string | undefined;
}
/** @deprecated 没有使用,使用的是 import { ModelOptionItem } from '@coze-studio/components'; */
/** @Deprecated is not used, import {ModelOptionItem} from '@code-studio/components' is used; */
export const OptionItem: React.FC<OptionItemProps> = ({
avatar,
descriptionGroupList,
@@ -66,7 +66,7 @@ export const OptionItem: React.FC<OptionItemProps> = ({
{descriptionGroupList?.length ? (
<Popover
trigger="hover"
// 加个 delay ,防止误触
// Add a delay to prevent accidental touch
mouseEnterDelay={1000 * 0.3}
className={'max-w-[224px] px-[12px] py-[8px]'}
content={

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
// TODO 为了联调先封装一个业务组件,后面再抽象成通用的request select
// TODO first encapsulates a business component for joint debugging, and then abstracts it into a general request select.
import React, { Suspense, lazy } from 'react';
import { InputNumber, Tooltip } from '@coze-arch/coze-design';

View File

@@ -68,7 +68,7 @@ export const ModelSelect: FC<ModelSelectProps> = ({
);
/**
* 根据 modelMeta 生成默认值
* Generate default values from modelMeta
*/
const getDefaultValue = useCallback(
({ modelType, value }: { modelType?: number; value?: object }) => {
@@ -111,7 +111,7 @@ export const ModelSelect: FC<ModelSelectProps> = ({
const { getNodeSetterId, concatTestId } = useNodeTestId();
const setterTestId = getNodeSetterId(testName || 'llm-select');
// 【运维平台】由于无法拉取到模型列表,下拉框会渲染不出来,因此这里直接将已有的模型值展示
// [Operation and maintenance platform] Since the model list cannot be pulled, the drop-down box will not be rendered, so the existing model values will be directly displayed here.
if (IS_BOT_OP && value) {
return <JsonViewer data={value} />;
}
@@ -135,7 +135,7 @@ export const ModelSelect: FC<ModelSelectProps> = ({
value.generationDiversity ?? defaultGenerationDiversity;
let _defaultValue;
// 如果是自定义,优先级:缓存的用户值 > 默认
// If custom, priority: cached user value > default
if (generationDiversity === GenerationDiversity.Customize) {
_defaultValue =
getDefaultValue({
@@ -154,7 +154,7 @@ export const ModelSelect: FC<ModelSelectProps> = ({
modelName: record.label as string,
modelType: record.value as number,
generationDiversity,
// 切换模型时不重置输出格式
// Do not reset the output format when switching models
responseFormat:
value?.responseFormat ?? _defaultValue?.responseFormat,
});

View File

@@ -33,18 +33,18 @@ export const getValueByType = <T,>(value, type?: ModelParamType): T => {
return _value;
};
// 内存级缓存
// memory level cache
const cacheData: {
[k: string]: unknown;
} = {
//记录展开/收起状态
//Record expanded/stowed status
expand: undefined,
};
export { cacheData };
/**
* 根据 modelParams 生成默认值
* 如果传入 value 则优先使用 value可以理解为对 value 做 min max 校验)
* Generate default values from modelParams
* If you pass in value, use value first (it can be understood as a min max check for value)
*/
export const generateDefaultValueByMeta = ({
modelParams,
@@ -65,20 +65,20 @@ export const generateDefaultValueByMeta = ({
modelParams?.forEach(p => {
const key = getCamelNameName(p.name ?? '');
// 优先从 value 中取,取不到再从 meta 里取。【自定义】默认值兜底
// Priority is given to taking from the value, and if you can't get it, you can take it from the meta. [Custom] The default value is the bottom.
let _v =
value?.[key] ??
p.default_val?.[generationDiversity] ??
p.default_val?.[GenerationDiversity.Customize];
// 防御代码:这里是为了校验后端返回默认值,理论上默认值必须合法,但万一呢。数字类型的默认值需要保证 max >= v >= min
// Defense code: This is to verify that the backend returns the default value. Theoretically, the default value must be legal, but what if. The default value of the number type needs to be guaranteed max > = v > = min.
if (
p.type &&
[ModelParamType.Float, ModelParamType.Int].includes(p.type)
) {
const { min, max } = p;
// 数字类型的,实在取不到值了就用 0
// For numeric types, if you really can't get the value, use 0.
if (['', undefined].includes(_v)) {
_v = 0;
}

View File

@@ -86,7 +86,7 @@ function useWorkflowNode(node: FlowNodeEntity) {
new WorkflowNode(node),
);
// 监听底层实例数据变化 并更新业务层实例
// Monitor the underlying instance data changes and update the business layer instance
useEffect(() => {
const updateWorkflowNode = () => {
startTransition(() => {

View File

@@ -95,7 +95,7 @@ export function CustomDragCard({
nodeVersionInfo: isPluginApiNodeTemplate(nodeTemplate)
? { pluginId: nodeTemplate.plugin_id, version: nodeTemplate.version }
: isSubWorkflowNodeTemplate(nodeTemplate)
? // workflow 的 version 信息在 drop 时通过接口获取
? // The version information of the workflow is obtained through the interface when dropping
{
workflowId: nodeTemplate.workflow_id,
pluginId: nodeTemplate.plugin_id,
@@ -133,7 +133,7 @@ export function CustomDragCard({
data-testid={testId}
data-node-type={nodeType}
>
{/* 拖拽过程中的节点副本,被下面的节点覆盖,因此不会触发 hover 之类的 */}
{/* The copy of the node during the drag process is overwritten by the node below, so no hover or the like is triggered */}
<div
ref={preview}
className={classNames(computedClassNames, styles['preview-card'])}
@@ -142,7 +142,7 @@ export function CustomDragCard({
>
{children}
</div>
{/* 可拖拽的节点,盖在节点副本上 */}
{/* Draggable nodes, overlaid on node replicas */}
{
<div
ref={!disabled ? drag : null}

View File

@@ -61,7 +61,7 @@ interface NodesContainerProps {
containerNode?: WorkflowNodeEntity;
adaptiveHeight?: number;
/**
* 更新正在添加节点的状态,此时 clickOutside 不会关闭节点面板,避免添加节点完成前面板触发 onClose 关闭,之前的连线不能销毁
* Update the status of the node being added. At this time, clickOutside will not close the node panel to avoid triggering onClose to close before the addition of the node is completed, and the previous connection cannot be destroyed.
* @param isAdding
* @returns
*/
@@ -119,7 +119,7 @@ export const NodeList = forwardRef<NodeListRefType, NodesContainerProps>(
}));
useEffect(() => {
const text = input.replaceAll(' ', ''); // 删除空格
const text = input.replaceAll(' ', ''); // Remove spaces
handleKeywordChange(text);
}, [input]);

View File

@@ -95,7 +95,7 @@ export const NodePanel: FC<NodePanelRenderProps> = props => {
const panelProps = props.panelProps as AddNodePanelProps;
const playground = usePlayground();
// 正在添加节点,为 true 时,不允许关闭节点面板
// Node is being added, when true, closing the node panel is not allowed
const isAddingNodeRef = useRef(false);
const { openPlugin, openWorkflow, openImageflow, updateAddNodePosition } =
useAddNodeModalContext();
@@ -119,7 +119,7 @@ export const NodePanel: FC<NodePanelRenderProps> = props => {
Math.max(topHeight, bottomHeight),
);
}
// 满足最大高度时不指定高度,由元素自动撑开
// When the maximum height is met, the height is not specified, and it is automatically stretched by the element
if (targetHeight === MAXMIUM_HEIGHT) {
return MAXMIUM_HEIGHT;
}
@@ -231,7 +231,7 @@ export const NodePanel: FC<NodePanelRenderProps> = props => {
useEffect(() => {
if (!panelProps.enableScrollClose) {
// 不启用滚动关闭时,不监听滚动事件
// Do not listen for scroll events when scrolling off is not enabled
return;
}
const disposer = playground.onScroll(() => {
@@ -254,7 +254,7 @@ export const NodePanel: FC<NodePanelRenderProps> = props => {
}, [templateState]);
useEffect(() => {
//刷新弹窗展示时关闭新增按钮 popover
//Close the new button popover when refreshing the pop-up window display
if (dependencyEntity?.refreshModalVisible) {
handleClose();
}

View File

@@ -63,7 +63,7 @@ export const PluginNodeList: FC<PluginNodeListProps> = ({
}) => {
const { keyword, enableDrag, onSelect, onAddingNode } = useNodePanelContext();
// 计算需要显示的节点列表,第一页 5 个,后面每页增加 4 个5,5+4,5+4+4,5+4+4+4...
// Calculate the list of nodes to be displayed, 5 on the first page, and 4 on each subsequent page (5, 5 + 4, 5 + 4 + 4, 5 + 4 + 4 + 4...)
const displayPluginNodeList = useMemo<PluginNodeListWithLoadMore>(() => {
const nodeList: PluginNodeListWithLoadMore = pluginNodeList;
if (!nodeList) {
@@ -74,7 +74,7 @@ export const PluginNodeList: FC<PluginNodeListProps> = ({
return nodeList;
}
const rawPageNum = (len - INIT_PAGE_SIZE) / LOAD_MORE_PAGE_SIZE;
// 如果 rawPageNum 有小数部分,
// If rawPageNum has a fractional part,
if (rawPageNum % 1 > 0) {
const displayLen =
Math.floor(rawPageNum) * LOAD_MORE_PAGE_SIZE + INIT_PAGE_SIZE;
@@ -84,7 +84,7 @@ export const PluginNodeList: FC<PluginNodeListProps> = ({
}, [pluginNodeList, hasMore]);
const [expandPluginIndex, setExpandPluginIndex] = useState<number>();
// tools 列表插入的位置
// Tools list insert location
const pluginToolsDisplayIndex = useMemo<number | undefined>(() => {
if (!isNumber(expandPluginIndex)) {
return;

View File

@@ -94,7 +94,7 @@ export const SearchResultNodeList: FC<{
searchResult: NodeSearchResult;
loading: boolean;
}> = ({ searchResult, loading }) => {
// (只有前端搜索节点 && loading) => 出小圈
// (Only front-end search node & & loading) = > out of small circle
const showSpinLoading = useMemo(
() =>
loading &&
@@ -102,7 +102,7 @@ export const SearchResultNodeList: FC<{
searchResult[0].dataType === NodeSearchSectionType.Atom,
[searchResult, loading],
);
// (无节点 || 前端搜索、后端搜索的两种节点都有 && loading) => 出大圈
// (No nodes | | Both nodes of front-end search and back-end search have & & loading) = > out of a large circle
const showMaskLoading = useMemo(
() =>
loading &&

View File

@@ -58,7 +58,7 @@ export const SubWorkflowCategoryList: FC<SubWorkflowCategoryListProps> = ({
try {
onAddingNode?.(true);
// 补全版本信息
// Complete version information
const versionName = await getWorkflowVersionByPluginId({
spaceId,
pluginId: nodeTemplate.plugin_id,

View File

@@ -29,7 +29,7 @@ interface NodePanelContextType {
getScrollContainer?: () => HTMLDivElement | undefined;
onLoadMore?: (id?: NodePanelSearchType, cursor?: string) => Promise<void>;
/**
* 更新正在添加节点的状态,此时 clickOutside 不会关闭节点面板
* Update the status of the node being added, clickOutside will not close the node panel at this time
* @param isAdding
* @returns
*/

View File

@@ -96,7 +96,7 @@ export const useFavoritePluginNodeList = (): {
const newList = uniqBy(prev.concat(moreList), 'plugin_id');
return newList;
});
// 更新 context 上保存的插件列表,下次打开节点面板时保持已经 load more 过的
// Update the list of plugins saved on the context and keep loading more the next time you open the node panel
context.favoritePlugins = {
favorite_products: context.favoritePlugins?.favorite_products?.concat(
resp?.favorite_products ?? [],

View File

@@ -35,7 +35,7 @@ export function Database() {
className="w-[16px] h-[16px] rounded-mini"
/>
),
// 运维平台直接展示 ID 即可,因为运维平台无法拉取到实际的数据库信息
// The operation and maintenance platform can directly display the ID, because the operation and maintenance platform cannot pull the actual database information.
label: IS_BOT_OP ? databaseID : database.tableName,
},
]

View File

@@ -42,7 +42,7 @@ const thumbnails = [
export function Model() {
const { data } = useWorkflowNode();
// 组件化表单字段name支持点语法 新版一般从inputs开始 这里需要兼容老版
// Component form field name supports point syntax. The new version generally starts from inputs. It needs to be compatible with the old version.
const model =
get(data, 'modelSetting.model') || get(data, 'inputs.modelSetting.model');

View File

@@ -52,7 +52,7 @@ import { DatabaseDeleteContent } from './database-delete-content';
import { DatabaseCreateContent } from './database-create-content';
import { DatabaseContent } from './database-content';
import { CommonContent } from './common-content';
// cli 脚本插入标识import请勿修改/删除此行注释
// CLI script insert ID (import), do not modify/delete this line comment
import styles from './index.module.less';
@@ -91,11 +91,11 @@ const ContentMap = {
[StandardNodeType.Api]: PluginContent,
[StandardNodeType.Variable]: VariableContent,
[StandardNodeType.JsonStringify]: JsonStringifyContent,
// cli 脚本插入标识registry请勿修改/删除此行注释
// The cli script inserts the identifier (registry), do not modify/delete this line comment
};
/**
* 节点内容区域
* Node content area
*/
export function Content() {
const { type } = useWorkflowNode();

View File

@@ -31,7 +31,7 @@ import {
import { type BoundSkills } from '@/nodes-v2/llm/skills/types';
/**
* 节点上缓存一份技能名称和icon的数据
* Cache a skill name and icon data on the node
* @param node
* @param fcParam
* @param skillsDetail

View File

@@ -18,7 +18,7 @@ import { VariableMergeItem } from './variable-merge-item';
import { useVariableMergeVariableTags } from './use-variable-merge-variable-tags';
/**
* 合并变量节点内容
* Merge variable node content
*/
export function VariableMergeContent() {
const mergeGroups = useVariableMergeVariableTags();

View File

@@ -32,7 +32,7 @@ import { VARIABLE_TYPE_ICON_MAP } from '../../fields/constants';
import { type VariableMergeGroup } from './types';
/**
* 获取合并变量的变量标签列表
* Get a list of variable labels for the merged variable
* @returns
*/
export function useVariableMergeVariableTags(): VariableMergeGroup[] {
@@ -54,7 +54,7 @@ export function useVariableMergeVariableTags(): VariableMergeGroup[] {
);
const isLiteral = ValueExpression.isLiteral(v);
// 校验变量是否有效
// Verify whether the variable is valid
const invalid =
!isLiteral &&
!variableService.getWorkflowVariableByKeyPath(
@@ -62,7 +62,7 @@ export function useVariableMergeVariableTags(): VariableMergeGroup[] {
{ node, checkScope: true },
);
// 是否为运行的输出变量
// Is it the output variable of the run?
const isOutput = isOutputVariable(
groupIndex,
index,
@@ -83,7 +83,7 @@ export function useVariableMergeVariableTags(): VariableMergeGroup[] {
})
.filter(v => v.type && VARIABLE_TYPE_ICON_MAP[v.type]);
// 类型取第一个变量的类型
// Type Take the type of the first variable
const type = variableTags[0]?.type;
const label = type ? VARIABLE_TYPE_ALIAS_MAP[type] : '';

View File

@@ -27,7 +27,7 @@ interface VariableMergeItemProps {
}
/**
* 变量合并项
* variable merge
*/
export const VariableMergeItem: FC<VariableMergeItemProps> = ({
mergeGroup,

View File

@@ -55,9 +55,9 @@ const Operator: FC<{
4: <IconCozGreaterEqual />,
5: <IconCozLess />,
6: <IconCozLessEqual />,
// 包含
// contain
7: <IconCozProperSuperset />,
// 不包含
// Do not include
8: <IconCozProperSupersetSlash />,
// isEmpty
9: <IconCozEqual />,

View File

@@ -36,9 +36,9 @@ interface SettingFieldsProps {
}
/**
* 数据库字段设置组件
* @param props.label 标题
* @param props.name 字段路径,用于从节点数据中获取字段设置值
* Database field setting component
* @Param props.label title
* @Param props.name field path to get field settings from node data
*/
export function DatabaseSettingFields({ label, name }: SettingFieldsProps) {
const { data: database, isLoading, error } = useCurrentDatabaseQuery();

View File

@@ -29,20 +29,20 @@ import { type StandardNodeType } from '@coze-workflow/base';
import { Port } from '../port';
/**
* 异常端口
* abnormal port
*/
export function ExceptionPort() {
const node = useCurrentEntity();
const portsData = node.getData<WorkflowNodePortsData>(WorkflowNodePortsData);
useEffect(() => {
// 动态端口的节点
// Nodes for dynamic ports
if (isSettingOnErrorDynamicPort(node.flowNodeType as StandardNodeType)) {
portsData.updateDynamicPorts();
return;
}
// 静态端口的节点
// Node on static port
portsData.updateStaticPorts([
{ type: 'input' },
{ type: 'output', portID: 'default' },

View File

@@ -15,7 +15,7 @@
*/
/**
* 意图识别节点,选项组件渲染
* Intent to identify nodes, option component rendering
*/
import { INTENT_NODE_MODE } from '@coze-workflow/nodes';

View File

@@ -18,8 +18,8 @@ import { useWorkflowNode } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
/**
* 这里要跟表单内的 knowledge list 用同一个 useDataSetInfos
* useDataSetInfos 内部会缓存数据,当表单内选中新的知识库时,不用重新发请求拉数据
* Use the same useDataSetInfos as the knowledge list in the form.
* useDataSetInfos will cache the data internally. When a new knowledge base is selected in the form, there is no need to resend the request to pull the data.
*/
import { useDataSetInfos } from '@/hooks';
@@ -38,7 +38,7 @@ export function Knowledge() {
>
<OverflowTagList
value={dataSets.map(d => ({
// 运维平台直接展示 ID 即可,因为运维平台无法拉取到实际的知识库信息
// The operation and maintenance platform can directly display the ID, because the operation and maintenance platform cannot pull the actual knowledge base information.
label: IS_BOT_OP ? d.dataset_id : d.name,
icon: (
<img className="w-[16px] h-[16px] rounded-mini" src={d.icon_url} />

View File

@@ -27,7 +27,7 @@ export const LabelWithTooltip = ({ customClassName, maxWidth, content }) => {
const textWidth = textRef.current?.offsetWidth;
setIsOverflowing(textWidth >= maxWidth);
}
}, [content, maxWidth]); // 依赖于文本内容,当文本内容变化时重新检查
}, [content, maxWidth]); // Depends on the text content, rechecking when the text content changes
return (
<Tooltip

View File

@@ -29,8 +29,8 @@ export function Model() {
useEffect(() => {
// fix:
// 右侧下拉框展示的模型名称取得是 name 字段,而不是 modelName
// 因此这里需要根据 modeType 找到原来模型,展示 name 字段,保持两侧显示一致
// The drop-down box on the right shows that the model name is obtained in the name field, not the modelName.
// Therefore, you need to find the original model according to the modeType, display the name field, and keep the display on both sides consistent.
if (data?.model?.modelType) {
const model = models.find(v => v.model_type === data?.model?.modelType);
if (model) {

View File

@@ -26,7 +26,7 @@ interface OutputsProps {
}
/**
* 节点输出
* Node output
*/
export function Outputs({
label = I18n.t('workflow_detail_node_output'),

View File

@@ -36,7 +36,7 @@ export interface TagProps {
}
export interface OverflowTagListProps<T extends TagProps = TagProps> {
/* tag 列表 */
/* tag list */
value?: T[];
enableTooltip?: boolean;
tagItemRenderer?: (tagData: T) => ReactNode;

View File

@@ -114,7 +114,7 @@ export function QuestionPairs() {
label={convertNumberToLetters(index)}
content={option?.name}
/>
{/* 对应 questionNodeRegistry options 表单项 */}
{/* Corresponding questionNodeRegistry options table entry */}
<Port id={`branch_${index}`} type="output" />
</Field>
))

View File

@@ -34,19 +34,19 @@ import {
import { useAvailableNodeVariables } from '../hooks/use-available-node-variables';
import { type VariableTagProps } from './variable-tag-list';
/** 查看表单值是否非法,如果设置了必填,但是没有相关值,则返回 true */
/** Check if the form value is illegal. If required is set, but there is no relevant value, return true. */
const checkInvalid = (
inputDef: VariableMetaDTO | undefined,
input: ValueExpression,
) => {
let invalid = false;
// 只针对必填参数做 invalid 判断
// Make invalid judgments only for required parameters
if (inputDef?.required) {
if (input?.type === ValueExpressionType.REF) {
// 引用必须存在 keypath 数组
// The reference must exist in the keypath array
invalid = !input?.content?.keyPath?.length;
} else if (input?.type === ValueExpressionType.LITERAL) {
// 指定值必须存在 content
// The specified value must contain content
invalid = input?.content === '' || isNil(input?.content);
}
}
@@ -60,8 +60,8 @@ export function useInputParametersVariableTags(
const variableService = useAvailableNodeVariables(node);
const { registry } = useWorkflowNode();
// 后端有部分模板的 inputParameters 设置成了 null此时不会走到默认赋值 [] 的逻辑
// 会导致下边相关方法报错,例如 Object.entries(inputParameters),这里做个兜底
// The inputParameters of some templates in the back end are set to null, and the logic of the default assignment [] will not be reached at this time.
// It will cause the following related methods to report an error, such as Object.entries (inputParameters), here is a bottom line
if (!inputParameters) {
return [];
}
@@ -83,12 +83,12 @@ export function useInputParametersVariableTags(
}
let viewType: ViewVariableType | undefined;
// 变量是否有效,默认有效
// Whether the variable is valid, it is valid by default
let invalid = false;
if (input?.rawMeta?.type) {
viewType = input?.rawMeta?.type;
// 引用变量的参数,需要判断变量是否存在,来确认有效
// To refer to the parameters of the variable, you need to determine whether the variable exists to confirm that it is valid
if (input && ValueExpression.isRef(input)) {
const variable = variableService.getWorkflowVariableByKeyPath(
(input?.content as RefExpressionContent)?.keyPath,
@@ -117,7 +117,7 @@ export function useInputParametersVariableTags(
) as VariableMetaDTO;
invalid = checkInvalid(inputDef, input);
// 子流程的参数定义,优先使用参数定义的类型
// Parameter definition for subprocesses, preferentially using the type of parameter definition
if (inputDef) {
viewType = variableUtils.DTOTypeToViewType(
inputDef.type as VariableTypeDTO,
@@ -129,7 +129,7 @@ export function useInputParametersVariableTags(
}
}
// 针对插件节点特殊处理
// Special handling for plug-in nodes
if (node.flowNodeType === StandardNodeType.Api) {
const nodeData = node.getData<WorkflowNodeData>(WorkflowNodeData);
const detail = nodeData?.getNodeData<StandardNodeType.Api>();
@@ -137,7 +137,7 @@ export function useInputParametersVariableTags(
v => v.name === name,
) as VariableMetaDTO;
// 如果存在参数定义,优先使用参数定义的类型
// If a parameter definition exists, the type of the parameter definition is preferred
if (inputDef) {
viewType = variableUtils.DTOTypeToViewType(inputDef.type, {
arrayItemType: inputDef?.schema?.type,
@@ -148,7 +148,7 @@ export function useInputParametersVariableTags(
invalid = checkInvalid(inputDef, input);
}
// 针对 BOT_USER_INPUT 变量,设置默认值类型
// For BOT_USER_INPUT variables, set the default value type
if (isUserInputStartParams(name) && !viewType) {
viewType = ViewVariableType.String;
}

Some files were not shown because too many files have changed in this diff Show More