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

@@ -16,7 +16,7 @@
import { StandardNodeType } from '@coze-workflow/base';
// 默认所有节点可用,可以自定义
// All nodes are available by default and can be customized.
export const getEnabledNodeTypes = (_params: {
loopSelected: boolean;
isProject: boolean;

View File

@@ -18,7 +18,7 @@ import { api, type InferEditorAPIFromPlugins } from '@coze-editor/editor/react';
import preset from '@coze-editor/editor/preset-code';
import { type EditorView } from '@codemirror/view';
// 忽略 readOnly 强制设置值
// Ignore readOnly to force a value
const forceSetValue =
({ view }: { view: EditorView }) =>
(value: string) => {

View File

@@ -35,7 +35,7 @@ export interface Output {
children?: Output[];
}
// javascript 为历史数据,目前只会有 python typescript
// Javascript is historical data, currently only python | typescript is available.
export type LanguageType = 'python' | 'typescript' | 'javascript';
export interface PreviewerProps {
@@ -67,7 +67,7 @@ export interface EditorProps {
onTestRun?: () => void;
testRunIcon?: ReactNode;
/**
* @deprecated onTestRunStateChange 已失效,线上也未使用到
* @Deprecated onTestRunStateChange has expired and is not used online
*/
onTestRunStateChange?: (status: string) => void;
}

View File

@@ -27,8 +27,8 @@ export function DemoComponent(props: { name: string }): JSX.Element {
const [foo] = useState('hello world');
const { name } = props;
return (
// font-bold 来自 taiwindcss
// 建议优先使用 taiwindcss
// Font-bold from taiwindcss
// It is recommended to use taiwindcss first.
<div className={classNames(s.foo, 'font-bold')}>
{foo} {name}!
<div>

View File

@@ -38,14 +38,14 @@ const hasHistory = (): boolean =>
type NavigateScene = 'publish' | 'exit';
/**
* 返回上一页逻辑
* Return to previous page logic
*/
export const useNavigateBack = () => {
const pageJumpResponse = usePageJumpResponse(PageType.WORKFLOW);
const { jump } = usePageJumpService();
const navigate = useNavigate();
/** 流程详情页路由更新会导致 pageJumpResponse 失效, 因此需要缓存一份原始版本 */
/** Process details page route update will invalidate pageJumpResponse, so you need to cache a copy of the original version */
const memoPageJumpResponse = useMemo(() => pageJumpResponse, []);
const navigateBack = async (
@@ -71,12 +71,12 @@ export const useNavigateBack = () => {
return;
}
// 跳转回bot详情页并带上场景参数
// Jump back to the bot details page with scene parameters
if (memoPageJumpResponse?.scene) {
if (scene === 'publish') {
let pluginID = plugin_id;
// 第一次发布流程时需要重新从接口获取pluginID
// When you publish the process for the first time, you need to retrieve the pluginID from the interface again.
if (!pluginID || pluginID === '0') {
const workflowRes = await workflowApi.GetCanvasInfo(
{
@@ -143,9 +143,9 @@ export const useNavigateBack = () => {
namespace: 'workflow',
eventName: 'workflow_navigate_back_to_list',
});
// 如果 history 就一个页面,此时 navigate(-1) 不生效,需要手动指定路径
// 目前先写死路径,缺点是需要跟随页面结构更改而更改,后面再优化更好的实现
// /library coze 2.0 混排资源列表页
// If history is only one page, navigate (-1) does not take effect at this time, you need to manually specify the path
// At present, the dead path is written first. The disadvantage is that it needs to be changed with the change of the page structure, and then optimized for a better implementation later.
// /library coze 2.0 mixed resource list page
navigate(`/space/${spaceId}/library`);
}
};

View File

@@ -22,23 +22,23 @@ import { OperateType } from '@coze-workflow/base/api';
import { I18n } from '@coze-arch/i18n';
import { Toast } from '@coze-arch/coze-design';
/** 流程详情页参数 */
/** Process details page parameters */
interface SearchParams {
workflow_id: string;
space_id: string;
/** 流程版本, 当多人协作时有版本概念, 单独设置时可预览对应版本流程 */
/** Process version, when multiple people cooperate, there is a version concept, and the corresponding version process can be previewed when set separately */
version?: string;
/** 是否要恢复到目标版本, 如果设置, 则流程草稿会自动设置到对应 version */
/** Whether to restore to the target version, if set, the process draft will be automatically set to the corresponding version */
set_version?: string;
/** 对应 version 的操作类型 */
/** Corresponding version of the operation type */
opt_type?: string;
/** 流程页面打开来源 */
/** Process page open source */
from?: WorkflowPlaygroundProps['from'];
/** 节点id 配置会自动定位到对应节点 */
/** The node id configuration will automatically locate to the corresponding node. */
node_id?: string;
/** 执行id 配置会展示对应执行结果 */
/** The execution id configuration will display the corresponding execution result. */
execute_id?: string;
/** 子流程执行id */
/** subprocess execution id */
sub_execute_id?: string;
}
@@ -85,7 +85,7 @@ export function usePageParams() {
needUpdateSearch = true;
}
// 强制设置历史版本
// Force setting of historical version
if (setVersion) {
newSearchParams.delete('set_version');
newSearchParams.delete('version');

View File

@@ -26,7 +26,7 @@ import {
import { usePageParams } from './hooks/use-page-params';
import { useNavigateBack } from './hooks';
// 添加节点放在工具栏了,原来侧边栏不需要了
// The added node is placed in the toolbar, but the original sidebar is no longer needed.
const EmptySidebar = React.forwardRef<AddNodeRef, unknown>(
(_props, _addNodeRef) => null,
);
@@ -48,7 +48,7 @@ export function WorkflowPage(): React.ReactNode {
const [initOnce, setInitOnce] = useState(false);
const { navigateBack } = useNavigateBack();
/** 是否只读模式, 来源于流程探索模块 */
/** Whether it is read-only mode, derived from the process exploration module */
const readonly = from === 'explore';
if (!workflowId || !spaceId) {
@@ -75,14 +75,14 @@ export function WorkflowPage(): React.ReactNode {
});
}
// onInit可能被多次调用 这里只需要执行一次
// onInit may be called multiple times, it only needs to be executed once
if (!initOnce) {
// 读取链接上的node_id参数 滚动到对应节点
// Read the node_id parameters on the link and scroll to the corresponding node
if (nodeId) {
workflowPlaygroundRef.current?.scrollToNode(nodeId);
}
// 读取execute_id 展示对应执行结果
// Read execute_id show the corresponding execution result
if (executeId) {
workflowPlaygroundRef.current?.showTestRunResult(
executeId,

View File

@@ -18,79 +18,79 @@
import { useState, useCallback, useEffect, useRef } from 'react';
interface IAudioPlayerOptions {
// 是否自动播放
// Whether to play automatically
autoPlay?: boolean;
// 是否循环播放
// Whether to loop
loop?: boolean;
// 播放结束回调
// end of play callback
onEnded?: () => void;
// 加载完成回调
// load completion callback
onLoad?: () => void;
}
interface IAudioPlayer {
// 是否正在播放
// Is it playing?
isPlaying: boolean;
// 是否暂停
// Whether to pause
isPaused: boolean;
// 是否停止
// Whether to stop
isStopped: boolean;
// 当前播放时间
// current playing time
currentTime: number;
// 音频总时长
// total audio duration
duration: number;
// 播放
// play
play: () => void;
// 暂停
// pause
pause: () => void;
// 停止
// Stop
stop: () => void;
// 切换播放暂停
// toggle playback pause
togglePlayPause: () => void;
// 跳转
// jump
seek: (time: number) => void;
}
/**
* 音频播放器 Hook
* @param src - 音频文件的
* @param options - 播放器配置选项
* @returns 音频播放器控制接口
* Audio Player Hook
* @Param src - audio files
* @Param options - player configuration options
* @Returns Audio Player Control Interface
*/
export const useAudioPlayer = (
src: File | string | undefined | null,
{ autoPlay, loop, onEnded, onLoad }: IAudioPlayerOptions = {},
): IAudioPlayer => {
// 播放状态管理
// playback state management
const [isPlaying, setIsPlaying] = useState(false);
const [isPaused, setIsPaused] = useState(false);
const [isStopped, setIsStopped] = useState(true);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
// 音频元素引用
// audio element reference
const audioRef = useRef<HTMLAudioElement | null>(null);
// 初始化音频元素和事件监听
// Initialize audio elements and event listeners
useEffect(() => {
if (!src) {
return;
}
const url = src instanceof Blob ? URL.createObjectURL(src) : src;
// 创建音频实例
// Create audio instance
const audio = new Audio(url);
audioRef.current = audio;
// 设置循环播放
// Set up loop playback
audio.loop = loop ?? false;
// 更新当前播放时间的处理函数
// Update the handler for the current playback time
const handleTimeUpdate = () => {
setCurrentTime(audio.currentTime);
};
// 元数据加载完成的处理函数(获取音频时长等信息)
// The processing function for the completion of metadata loading (to obtain information such as audio duration)
const handleLoadedMetadata = () => {
setDuration(audio.duration);
setCurrentTime(0);
@@ -101,12 +101,12 @@ export const useAudioPlayer = (
}
if (src instanceof Blob) {
// 释放音频文件的 URL避免内存泄漏
// Release the URL of the audio file to avoid memory leaks
URL.revokeObjectURL(url);
}
};
// 播放结束的处理函数
// Handler function for end of playback
const handleEnded = () => {
setIsPlaying(false);
setIsStopped(true);
@@ -114,12 +114,12 @@ export const useAudioPlayer = (
onEnded?.();
};
// 添加事件监听器
// Add event listeners
audio.addEventListener('loadedmetadata', handleLoadedMetadata);
audio.addEventListener('timeupdate', handleTimeUpdate);
audio.addEventListener('ended', handleEnded);
// 清理函数:组件卸载时移除事件监听并释放资源
// Cleanup function: removes event listeners and frees resources when a component is unloaded
return () => {
audio.removeEventListener('loadedmetadata', handleLoadedMetadata);
audio.removeEventListener('timeupdate', handleTimeUpdate);
@@ -131,7 +131,7 @@ export const useAudioPlayer = (
};
}, [src, autoPlay, loop, onEnded, onLoad]);
// 播放控制函数
// playback control function
const play = useCallback(() => {
if (audioRef.current) {
audioRef.current.play();
@@ -141,7 +141,7 @@ export const useAudioPlayer = (
}
}, []);
// 暂停控制函数
// pause control function
const pause = useCallback(() => {
if (audioRef.current) {
audioRef.current.pause();
@@ -150,7 +150,7 @@ export const useAudioPlayer = (
}
}, []);
// 停止控制函数
// Stop control function
const stop = useCallback(() => {
if (audioRef.current) {
audioRef.current.pause();
@@ -161,7 +161,7 @@ export const useAudioPlayer = (
}
}, []);
// 切换播放/暂停状态
// Switch play/pause state
const togglePlayPause = useCallback(() => {
if (isPlaying) {
pause();
@@ -170,7 +170,7 @@ export const useAudioPlayer = (
}
}, [isPlaying, play, pause]);
// 跳转到指定时间
// Jump to the specified time
const seek = useCallback((time: number) => {
if (audioRef.current) {
audioRef.current.currentTime = time;
@@ -178,7 +178,7 @@ export const useAudioPlayer = (
}
}, []);
// 返回音频播放器控制接口
// Back to Audio Player Control Interface
return {
isPlaying,
isPaused,

View File

@@ -27,7 +27,7 @@ import {
describe('with-query-client.tsx', () => {
beforeEach(() => {
// 清理 QueryClient 缓存
// Clean up the QueryClient cache
workflowQueryClient.clear();
});
@@ -89,10 +89,10 @@ describe('with-query-client.tsx', () => {
const WrappedComponent = withQueryClient(TestComponent);
const { container } = render(<WrappedComponent />);
// 初始加载状态
// initial loading state
expect(container.innerHTML).toContain('Loading...');
// 等待数据加载完成
// Wait for data loading to complete
await waitFor(() => {
expect(container.innerHTML).toContain('test data');
});
@@ -198,10 +198,10 @@ describe('with-query-client.tsx', () => {
const WrappedComponent = withQueryClient(ParentComponent);
const { container } = render(<WrappedComponent />);
// 初始加载状态
// initial loading state
expect(container.innerHTML).toContain('Loading Parent...');
// 等待所有查询完成
// Wait for all queries to complete
await waitFor(() => {
expect(container.innerHTML).toContain('parent data');
expect(container.innerHTML).toContain('child data');

View File

@@ -28,7 +28,7 @@ vi.mock('../../src/entities', async () => {
const actual = await vi.importActual('../../src/entities');
return {
...actual,
// 如果需要mock其他实体可以在这里添加
// If you need to mock other entities, you can add them here.
};
});
@@ -198,7 +198,7 @@ describe('WorkflowNode', () => {
const newData = { test: 'new data' };
workflowNode.setData(newData);
// 验证原始数据没有被修改
// Verify that the original data source has not been modified
expect(newData).toEqual({ test: 'new data' });
expect(mockFormItem.value).toEqual({ test: 'new data' });
});

View File

@@ -56,7 +56,7 @@ vi.mock('../../src/utils', () => ({
describe('useNodeTestId', () => {
beforeEach(() => {
vi.clearAllMocks();
// 设置 concatTestId 的默认行为
// Set the default behavior of concatTestId
mockConcatTestId.mockImplementation((...args: string[]) => args.join('.'));
});
@@ -99,8 +99,8 @@ describe('useNodeTestId', () => {
it('应该返回正确的节点设置器 ID', () => {
mockUseCurrentEntity.mockReturnValue({ id: '123' });
mockConcatTestId
.mockReturnValueOnce('playground.node.123') // 用于 getNodeTestId
.mockReturnValueOnce('playground.node.123.llm'); // 用于 getNodeSetterId
.mockReturnValueOnce('playground.node.123') // For getNodeTestId
.mockReturnValueOnce('playground.node.123.llm'); // For getNodeSetterId
const { result } = renderHook(() => useNodeTestId());
@@ -140,7 +140,7 @@ describe('useNodeTestId', () => {
});
it('应该在多个组件中返回不同的节点测试 ID', () => {
// 第一个组件
// The first component
mockUseCurrentEntity.mockReturnValue({ id: '123' });
mockConcatTestId.mockReturnValueOnce('playground.node.123');
@@ -148,7 +148,7 @@ describe('useNodeTestId', () => {
expect(result1.current.getNodeTestId()).toBe('playground.node.123');
expect(mockConcatTestId).toHaveBeenCalledWith('playground.node', '123');
// 第二个组件
// The second component
mockUseCurrentEntity.mockReturnValue({ id: '456' });
mockConcatTestId.mockReturnValueOnce('playground.node.456');
@@ -158,11 +158,11 @@ describe('useNodeTestId', () => {
});
it('应该在多个组件中返回不同的节点设置器 ID', () => {
// 第一个组件
// The first component
mockUseCurrentEntity.mockReturnValue({ id: '123' });
mockConcatTestId
.mockReturnValueOnce('playground.node.123') // 用于 getNodeTestId
.mockReturnValueOnce('playground.node.123.llm'); // 用于 getNodeSetterId
.mockReturnValueOnce('playground.node.123') // For getNodeTestId
.mockReturnValueOnce('playground.node.123.llm'); // For getNodeSetterId
const { result: result1 } = renderHook(() => useNodeTestId());
expect(result1.current.getNodeSetterId('llm')).toBe(
@@ -179,11 +179,11 @@ describe('useNodeTestId', () => {
'llm',
);
// 第二个组件
// The second component
mockUseCurrentEntity.mockReturnValue({ id: '456' });
mockConcatTestId
.mockReturnValueOnce('playground.node.456') // 用于 getNodeTestId
.mockReturnValueOnce('playground.node.456.llm'); // 用于 getNodeSetterId
.mockReturnValueOnce('playground.node.456') // For getNodeTestId
.mockReturnValueOnce('playground.node.456.llm'); // For getNodeSetterId
const { result: result2 } = renderHook(() => useNodeTestId());
expect(result2.current.getNodeSetterId('llm')).toBe(

View File

@@ -22,7 +22,7 @@ import { useWorkflowStore } from '../../../src/store/workflow';
describe('useWorkflowStore', () => {
beforeEach(() => {
// 重置 store 状态
// Reset store state
useWorkflowStore.setState({
nodes: [],
edges: [],
@@ -63,7 +63,7 @@ describe('useWorkflowStore', () => {
});
it('应该能够重置为空数组', () => {
// 先设置一些数据
// Let's set up some data.
useWorkflowStore.getState().setNodes([
{
id: '1',
@@ -75,7 +75,7 @@ describe('useWorkflowStore', () => {
},
]);
// 设置为空数组
// Set to empty array
useWorkflowStore.getState().setNodes([]);
expect(useWorkflowStore.getState().nodes).toEqual([]);
});
@@ -99,7 +99,7 @@ describe('useWorkflowStore', () => {
});
it('应该能够重置为空数组', () => {
// 先设置一些数据
// Let's set up some data.
useWorkflowStore.getState().setEdges([
{
sourceNodeID: '1',
@@ -107,7 +107,7 @@ describe('useWorkflowStore', () => {
},
]);
// 设置为空数组
// Set to empty array
useWorkflowStore.getState().setEdges([]);
expect(useWorkflowStore.getState().edges).toEqual([]);
});
@@ -115,11 +115,11 @@ describe('useWorkflowStore', () => {
describe('setIsCreatingWorkflow', () => {
it('应该正确设置创建状态', () => {
// 设置为 true
// Set to true
useWorkflowStore.getState().setIsCreatingWorkflow(true);
expect(useWorkflowStore.getState().isCreatingWorkflow).toBe(true);
// 设置为 false
// Set to false
useWorkflowStore.getState().setIsCreatingWorkflow(false);
expect(useWorkflowStore.getState().isCreatingWorkflow).toBe(false);
});

View File

@@ -25,7 +25,7 @@ import {
describe('node-type', () => {
describe('StandardNodeType', () => {
it('应该包含所有预定义的节点类型', () => {
// 测试一些关键的节点类型
// Test some key node types
expect(StandardNodeType.Start).toBe('1');
expect(StandardNodeType.End).toBe('2');
expect(StandardNodeType.LLM).toBe('3');

View File

@@ -29,7 +29,7 @@ describe('is-general-workflow', () => {
});
it('应该在 flowMode 为其他值时返回 false', () => {
// 测试其他可能的 WorkflowMode
// Test other possible WorkflowMode values
expect(isGeneralWorkflow(WorkflowMode.Imageflow)).toBe(false);
});
});

View File

@@ -27,17 +27,17 @@ vi.mock('@coze-arch/bot-utils', () => ({
typeSafeJSONParse: (str: string) => {
try {
const result = JSON.parse(str);
// 如果是批处理数据,确保返回数组类型
// If it is batch data, make sure to return an array type
if (str === 'invalid json') {
return str;
}
// 如果是批处理数据,确保返回数组类型
// If it is batch data, make sure to return an array type
if (str.includes('batch')) {
return Array.isArray(result) ? result : [];
}
return result;
} catch {
// 如果是批处理数据,返回空数组
// If it is batch data, return an empty array
if (str.includes('batch')) {
return [];
}
@@ -110,7 +110,7 @@ describe('default-parser', () => {
expect(result.nodeType).toBe(StandardNodeType.LLM);
expect(result.isBatch).toBe(false);
expect(result.caseResult).toHaveLength(1);
expect(result.caseResult?.[0].dataList).toHaveLength(3); // 输入、原始输出、最终输出
expect(result.caseResult?.[0].dataList).toHaveLength(3); // Input, original output, final output
});
it('应该正确解析 Code 节点结果', () => {
@@ -122,7 +122,7 @@ describe('default-parser', () => {
const result = defaultParser(nodeResult, workflowSchema);
expect(result.nodeType).toBe(StandardNodeType.Code);
expect(result.caseResult?.[0].dataList).toHaveLength(3); // 输入、原始输出、最终输出
expect(result.caseResult?.[0].dataList).toHaveLength(3); // Input, original output, final output
});
it('应该正确解析 Start 节点结果', () => {
@@ -134,7 +134,7 @@ describe('default-parser', () => {
const result = defaultParser(nodeResult, workflowSchema);
expect(result.nodeType).toBe(StandardNodeType.Start);
expect(result.caseResult?.[0].dataList).toHaveLength(1); // 只有输入
expect(result.caseResult?.[0].dataList).toHaveLength(1); // input only
});
it('应该正确解析 End 节点结果', () => {
@@ -154,7 +154,7 @@ describe('default-parser', () => {
const result = defaultParser(nodeResult, workflowSchema);
expect(result.nodeType).toBe(StandardNodeType.End);
expect(result.caseResult?.[0].dataList).toHaveLength(2); // 输出变量、回答内容
expect(result.caseResult?.[0].dataList).toHaveLength(2); // Output variables, answer content
});
it('应该正确解析 Message 节点结果', () => {
@@ -166,7 +166,7 @@ describe('default-parser', () => {
const result = defaultParser(nodeResult, workflowSchema);
expect(result.nodeType).toBe(StandardNodeType.Output);
expect(result.caseResult?.[0].dataList).toHaveLength(2); // 输出变量、回答内容
expect(result.caseResult?.[0].dataList).toHaveLength(2); // Output variables, answer content
});
it('应该正确解析包含图片的输出', () => {
@@ -256,7 +256,7 @@ describe('default-parser', () => {
it('应该正确处理 Text 节点的原始输出', () => {
const nodeResult = createMockNodeResult('1', {
raw_output: '{"key": "value"}', // 即使是有效的 JSON 字符串也应该保持原样
raw_output: '{"key": "value"}', // Even valid JSON strings should remain intact
});
const workflowSchema = createMockWorkflowSchema(
'1',
@@ -272,7 +272,7 @@ describe('default-parser', () => {
it('应该正确处理不存在的节点类型', () => {
const nodeResult = createMockNodeResult('1');
const workflowSchema = createMockWorkflowSchema('2'); // 不同的节点 ID
const workflowSchema = createMockWorkflowSchema('2'); // Different node IDs
const result = defaultParser(nodeResult, workflowSchema);
expect(result.nodeType).toBeUndefined();

View File

@@ -184,7 +184,7 @@ describe('ref-input-parameters-parser', () => {
param4: undefined,
param5: {
type: 'string',
value: {}, // 空的 value 对象
value: {}, // Empty value object
},
},
];

View File

@@ -21,7 +21,7 @@ export * from '@coze-arch/bot-api/workflow_api';
export { withQueryClient, workflowQueryClient } from './with-query-client';
/** 运营接口平台,会替换权限验证 */
/** Operating the interface platform will replace permission verification */
const workflowOperationApiNameMap = {
GetHistorySchema: 'OPGetHistorySchema',
GetWorkFlowProcess: 'OPGetWorkFlowProcess',

View File

@@ -14,35 +14,35 @@
* limitations under the License.
*/
/** 空方法 */
/** empty method */
export const EmptyFunction = () => {
/** 空方法 */
/** empty method */
};
export const EmptyAsyncFunction = () => Promise.resolve();
/** 公共空间 ID */
/** Public space ID */
export const PUBLIC_SPACE_ID = '999999';
/** BOT_USER_INPUT 变量名 */
/** BOT_USER_INPUT variable name */
export const BOT_USER_INPUT = 'BOT_USER_INPUT';
/** USER_INPUT 参数,新版 BOT_USER_INPUT 参数,作用和 BOT_USER_INPUT 相同,Coze2.0 Chatflow 需求引入 */
/** USER_INPUT parameters, the new version BOT_USER_INPUT parameters, the same function as BOT_USER_INPUT, Coze2.0 Chatflow requirements are introduced */
export const USER_INPUT = 'USER_INPUT';
/** CONVERSATION_NAME 变量名start 节点会话名称入参 */
/** CONVERSATION_NAME variable name, start node session name imported parameter */
export const CONVERSATION_NAME = 'CONVERSATION_NAME';
/**
* workflow 名称最大字符数
* Workflow name Maximum number of characters
*/
export const WORKFLOW_NAME_MAX_LEN = 30;
/**
* 工作流命名正则
* workflow naming regular
*/
export const WORKFLOW_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_]{0,63}$/;
/**
* 节点测试ID前缀
* Node test ID prefix
*/
export const NODE_TEST_ID_PREFIX = 'playground.node';

View File

@@ -15,7 +15,7 @@
*/
/**
* 业务层流程节点类 (对节点业务规则的封装)
* Business layer process node class (encapsulation of node business rules)
*/
import {
FlowNodeErrorData,
@@ -69,7 +69,7 @@ export class WorkflowNode {
}
}
// 这个方法建议暂时先不要使用 原因是该方法没有体现业务逻辑 只是底层方法的简化
// This method is recommended not to be used for the time being, because it does not reflect business logic, but is a simplification of the underlying method
protected getFormValueByPathEnds<T = unknown>(
pathEnds: string,
): T | undefined {

View File

@@ -21,21 +21,21 @@ import { concatTestId } from '../utils';
import { NODE_TEST_ID_PREFIX } from '../constants';
/**
* 仅限在 node 内使用
* Only used within the node
*/
type UseNodeTestId = () => {
/**
* 返回当前节点的 test-id也就是当前节点的 node id
* Returns the test-id of the current node, which is the node id of the current node.
* 'playground.11001
*/
getNodeTestId: () => string;
/**
* 返回当前 setter 的 test-id会自动带上节点的 test-id
* Returns the test-id of the current setter, which will automatically bring the test-id of the node.
* 'playground.11001.llm'
*/
getNodeSetterId: (setterName: string) => string;
/**
* 连接两个 test-id生成一个新的 test-id
* Connect two test-ids to generate a new test-id.
* ('a', 'b') => 'a.b'
*/
concatTestId: typeof concatTestId;
@@ -55,17 +55,17 @@ export const useNodeTestId: UseNodeTestId = () => {
return {
/**
* 返回当前节点的 test-id也就是当前节点的 node id
* Returns the test-id of the current node, which is the node id of the current node.
* 'playground.11001
*/
getNodeTestId,
/**
* 返回当前 setter 的 test-id会自动带上节点的 test-id
* Returns the test-id of the current setter, which will automatically bring the test-id of the node.
* 'playground.11001.llm'
*/
getNodeSetterId: setterName => concatTestId(getNodeTestId(), setterName),
/**
* 连接两个 test-id生成一个新的 test-id
* Connect two test-ids to generate a new test-id.
* ('a', 'b') => 'a.b'
*/
concatTestId,

View File

@@ -15,7 +15,7 @@
*/
/**
* 抽象类
* abstract class
*/
export {
ValidationService,

View File

@@ -29,20 +29,20 @@ export const useValidationServiceStore = <T>(
) => useValidationService().store(selector);
export interface ValidateError {
// 错误描述
// error description
errorInfo: string;
// 错误等级
// error level
errorLevel: FeedbackStatus;
// 错误类型: 节点 / 连线
// Error Type: Node/Connection
errorType: 'node' | 'line';
// 节点id
// Node ID
nodeId: string;
// 若为连线错误,还需要目标节点来确认这条连线
// In the case of a connection error, the target node is also required to confirm the connection
targetNodeId?: string;
}
export interface WorkflowValidateError {
workflowId: string;
/** 流程名 */
/** process name */
name?: string;
errors: ValidateErrorMap;
}
@@ -61,12 +61,12 @@ export interface ValidateResultV2 {
export interface ValidationState {
/**
* @deprecated 请使用 errorsV2
* @deprecated Please use errorsV2
*/
errors: ValidateErrorMap;
/** 按照流程归属分类的错误 */
/** Errors classified by process attribution */
errorsV2: WorkflowValidateErrorMap;
/** 正在校验中 */
/** Verifying now. */
validating: boolean;
}
@@ -74,48 +74,48 @@ export interface ValidationService {
store: UseBoundStoreWithEqualityFn<StoreApi<ValidationState>>;
/**
* 前端流程校验,包括节点、表单、端口等
* Front-end process validation, including nodes, forms, ports, etc
*/
validateWorkflow: () => Promise<ValidateResult>;
/**
* 对节点的校验,包括表单、端口等
* Verification of nodes, including forms, ports, etc
*/
validateNode: (node: WorkflowNodeEntity) => Promise<ValidateResult>;
/**
* @deprecated 请使用 validateSchemaV2
* 流程定义合法性校验,通常为后端校验
* @Deprecated please use validateSchemaV2
* Process definition validation, usually backend validation
*/
validateSchema: () => Promise<ValidateResult>;
/**
* 流程定义合法性校验,通常为后端校验
* Process definition validation, usually backend validation
*/
validateSchemaV2: () => Promise<ValidateResultV2>;
/**
* 获取指定 id 的错误列表
* Gets the error list for the specified id
*/
getErrors: (id: string) => ValidateError[];
/**
* @deprecated 请使用 setErrorsV2
* 设置错误
* @deprecated Please use setErrorsV2
* setting error
* @param errors
* @param force 强制覆盖所有错误
* @param force to overwrite all errors
*/
setErrors: (errors: ValidationState['errors'], force?: boolean) => void;
/**
* 设置错误
* setting error
* @param errors
* @returns
*/
setErrorsV2: (errors: ValidationState['errorsV2']) => void;
/**
* 清空所有错误
* Clear all errors
*/
clearErrors: () => void;
/** 线条是否存在错误 */
/** Is there an error in the line? */
isLineError: (fromId: string, toId?: string) => boolean;
get validating(): boolean;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
// workflow store,目前保存 flow 的 nodes edges 数据
// Workflow store, currently holds the nodes and edges data of the flow
import { devtools } from 'zustand/middleware';
import { create } from 'zustand';
@@ -24,13 +24,13 @@ import {
} from '@flowgram-adapter/free-layout-editor';
interface WorkflowStoreState {
/** 节点数据 */
/** node data */
nodes: WorkflowNodeJSON[];
/** 边数据 */
/** edge data */
edges: WorkflowEdgeJSON[];
/** 是否在创建 workflow */
/** Are you creating a workflow? */
isCreatingWorkflow: boolean;
}

View File

@@ -18,12 +18,12 @@ import { ViewVariableType } from './view-variable-type';
import { type DTODefine, type InputValueDTO } from './dto';
/**
* BlockInput是后端定义的类型,对应的就是 InputValueDTO
* BlockInput is a type defined by the backend, which corresponds to InputValueDTO.
*/
export type BlockInput = InputValueDTO;
/**
* BlockInput 转换方法
* BlockInput conversion method
*/
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace BlockInput {

View File

@@ -26,7 +26,7 @@ export enum ConditionLogicDTO {
export type ConditionOperator =
| 'EQUAL' // "="
| 'NOT_EQUAL' // "<>" "!="
| 'NOT_EQUAL' // "< >" or "! ="
| 'GREATER_THAN' // ">"
| 'LESS_THAN' // "<"
| 'GREATER_EQUAL' // ">="
@@ -35,7 +35,7 @@ export type ConditionOperator =
| 'NOT_IN' // "NOT IN"
| 'IS_NULL' // "IS NULL"
| 'IS_NOT_NULL' // "IS NOT NULL"
| 'LIKE' // "LIKE" 模糊匹配字符串
| 'NOT_LIKE' // "NOT LIKE" 反向模糊匹配
| 'BE_TRUE' // "BE TRUE" 布尔值为 true
| 'BE_FALSE'; // "BE FALSE" 布尔值为 false
| 'LIKE' // "LIKE" fuzzy match string
| 'NOT_LIKE' // "NOT LIKE" inverse fuzzy match
| 'BE_TRUE' // "BE TRUE" is true
| 'BE_FALSE'; // "BE FALSE" Boolean value false

View File

@@ -37,7 +37,7 @@ export interface WorkflowDatabase {
}
/**
* 数据库配置字段
* Database configuration field
*/
export interface DatabaseSettingField {
fieldID: number;
@@ -66,7 +66,7 @@ export type DatabaseSettingFieldDTO = [
];
/**
* 数据库条件
* database conditions
*/
export type DatabaseConditionOperator = ConditionOperator;
export type DatabaseConditionLeft = string;
@@ -89,7 +89,7 @@ export interface DatabaseConditionLeftDTO {
}
export interface DatabaseConditionOperatorDTO {
// 对操作符的翻译前后端没有统一
// Translation of operators is not uniform between front and back ends
name: 'operation';
input: {
type: 'string';

View File

@@ -15,7 +15,7 @@
*/
/**
* 这些是迁移的变量定义,可以不用关注
* These are the variable definitions for migration, you can ignore them.
*/
import { type ValueExpressionRawMeta } from './vo';
@@ -63,7 +63,7 @@ export namespace DTODefine {
rawMeta?: ValueExpressionRawMeta;
}
// schema string 时表示原始的 FDL 类型export type ListVariableSchema = VariableTypeDef & Omit<VariableOption, 'name'>
// Represents the original FDL type when schema is string export type ListVariableSchema = VariableTypeDef & Omit < VariableOption, 'name' >
export type ObjectVariableSchema = InputVariableDTO[];
export type ListVariableSchema = VariableTypeDef &
Omit<VariableOption, 'name'>;
@@ -109,21 +109,21 @@ export namespace DTODefine {
}
/**
* 后端定义的表达式格式
* Expression formats defined by the backend
* @example
* - literal
* {
* type: 'string',
* value: {
* type: 'liteal',
* content: '浙江'
* Content: 'Zhejiang'
* }
* }
*
* - ref
* // 普通引用类型
* //Common reference typery reference type
* {
* type: 'string', // 由引用的变量类型判断
* Type: 'string',//Determined by the type of the referenced variable the type of variable being referenced
* value: {
* type: 'ref',
* content: {
@@ -134,10 +134,10 @@ export namespace DTODefine {
* }
* }
*
* // list or object 引用类型
* //list or object reference typerence type
* {
* type: 'list', // 由引用的变量路径的最后一个值类型判断, 如果list.a.c, 则为c的格式
* schema: { // 只有list和object有schema
* Type: 'list',//Determined by the last value type of the referenced variable path, if list.a.c, the format of che referenced variable path, if list.a.c, the format of c
* Schema : { // only list and object have schemasts have schemas
* type: 'object',
* schema: [
* { name: 'role', type: 'string' },
@@ -149,7 +149,7 @@ export namespace DTODefine {
* content: {
* source: 'block-output',
* blockID: '1002',
* name: 'list.a.c' // 这里存的是引用的路径
* Name: 'list.a.c'//Here is the path of the referencee path to the reference
* },
* }
* }
@@ -168,7 +168,7 @@ export interface ValueExpressionDTO {
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace ValueExpressionDTO {
/**
* 空引用
* null reference
*/
export function createEmpty(): ValueExpressionDTO {
return {
@@ -219,7 +219,7 @@ export enum AssistTypeDTO {
}
/**
* 后端的变量格式
* Variable format for the backend
* @example
* 1. simple
* {
@@ -256,19 +256,19 @@ export enum AssistTypeDTO {
*/
export interface VariableMetaDTO {
/**
* 变量类型
* Variable type
*/
type: VariableTypeDTO;
/**
* 辅助类型string 类型的变量可以是 file 或者 image
* Auxiliary types, such as: string type variables can be file or image
*/
assistType?: AssistTypeDTO;
/**
* 变量名,在节点内不可重复
* Variable names are not repeatable within a node
*/
name: string;
/**
* 变量数据结构,仅object list 类型变量有
* Variable data structures, only object and list variables are available
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
schema?: any; // BlockVariableDefine.ObjectVariableSchema | BlockVariableDefine.ListVariableSchema
@@ -282,7 +282,7 @@ export interface VariableMetaDTO {
export interface BatchDTOInputList {
name?: string;
input: ValueExpressionDTO;
// 初始化后存在,服务端数据不存在
// Exists after initialization, server level data does not exist
id?: string;
}

View File

@@ -67,7 +67,7 @@ export {
type BasicStandardNodeTypes,
} from './node-type';
export { type WorkflowJSON, type WorkflowNodeJSON } from './node';
// !Notice data-set.ts 在用 io-ts 做运行时类型检查,禁止直接导出,避免 io-ts 被打到其它页面中
// ! Notice data-set.ts is using io-ts for runtime type checking, and direct export is prohibited to avoid io-ts being hit into other pages
// export { datasetParams, type DatasetParams } from './data-set';
import { InputType } from '@coze-arch/bot-api/developer_api';
@@ -78,7 +78,7 @@ import {
import { AssistTypeDTO } from './dto';
export { type ValueOf, type WithCustomStyle } from './utils';
// 和后端定义撞了,注释
// Bumped with the back-end definition, comment
export { type WorkflowInfo as FrontWorkflowInfo } from './workflow';
export {
@@ -88,8 +88,8 @@ export {
} from './registry';
/**
* 参数类型展示文案
* @tips workflow 编辑页请使用 {PARAM_TYPE_ALIAS_MAP}
* Parameter type display copy
* @Tips workflow edit page please use {PARAM_TYPE_ALIAS_MAP}
*/
export const PARAM_TYPE_LABEL_MAP: Record<InputType, string> = {
[InputType.String]: 'String',
@@ -116,9 +116,9 @@ export const STRING_ASSIST_TYPE_LABEL_MAP = {
export enum WorkflowExecStatus {
DEFAULT = 'default',
/** 执行中 */
/** in progress */
EXECUTING = 'executing',
/** 执行结束(此时依然有执行结束 banner且工作流为 disable 状态) */
/** End of execution (there is still an end of execution banner at this time, and the workflow is disabled) */
DONE = 'done',
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
// 跟后端约定好的协议workflow 后端不感知。对应 api/bot/get_type_list 接口中 response.data.modal_list[*].model_params[*].default 的 key
// The agreement agreed with the backend is not perceived by the workflow backend. Corresponding to the key response.data modal_list [*] .model_params [*] .default in the api/bot/get_type_list interface
export enum GenerationDiversity {
Customize = 'default_val',
Creative = 'creative',

View File

@@ -15,7 +15,7 @@
*/
/**
* 节点基础类型定义
* Node base type definition
*/
export enum StandardNodeType {
Start = '1',
@@ -38,32 +38,32 @@ export enum StandardNodeType {
SetVariable = '20',
Loop = '21',
Intent = '22',
// 对于新节点,可以使用文本的形式来编写 StandardNodeType不再依赖 NodeTemplateType
// For new nodes, StandardNodeType can be written in text form, without relying on NodeTemplateType.
ImageCanvas = '23',
SceneChat = '25',
SceneVariable = '24',
/** 长期记忆long term memory */
/** Long term memory */
LTM = '26',
/** 数据库写入节点 */
/** database write node */
DatasetWrite = '27',
Batch = '28',
Continue = '29',
// 输入节点
// input node
Input = '30',
Comment = '31',
// 变量聚合
// variable aggregation
VariableMerge = '32',
// 查询消息列表
// Query message list
QueryMessageList = '37',
// 清空上下文节点
// clear the context node
ClearContext = '38',
// 创建会话节点
// Create a session node
CreateConversation = '39',
// 触发器 CURD 4个节点
// Trigger CURD 4 nodes
// TriggerCreate = '33',
// TriggerUpdate = '34',
TriggerUpsert = '34',
@@ -72,34 +72,34 @@ export enum StandardNodeType {
VariableAssign = '40',
// http 节点
// HTTP Node
Http = '45',
// 数据库 crud 节点
// Database crud node
DatabaseUpdate = '42',
DatabaseQuery = '43',
DatabaseDelete = '44',
DatabaseCreate = '46',
// 更新会话
// Update session
UpdateConversation = '51',
// 删除会话
// Delete session
DeleteConversation = '52',
// 查询会话列表
// Query session list
QueryConversationList = '53',
// 查询会话历史
// Query session history
QueryConversationHistory = '54',
// 创建消息(某个会话)
// Create a message (a conversation)
CreateMessage = '55',
// 更新消息(某个会话的某个消息)
// Update message (a message for a session)
UpdateMessage = '56',
// 删除消息(某个会话的某个消息)
// Delete a message (a message for a session)
DeleteMessage = '57',
JsonStringify = '58',
@@ -107,7 +107,7 @@ export enum StandardNodeType {
}
/**
* 除了 Api、SubWorkflowImageflow 之外的基础节点类型
* Basic node types other than APIs, SubWorkflow, and Imageflow
*/
export type BasicStandardNodeTypes = Exclude<
StandardNodeType,
@@ -116,7 +116,7 @@ export type BasicStandardNodeTypes = Exclude<
| StandardNodeType.SubWorkflow
>;
/** 节点展示排序 */
/** Node display sorting */
export const NODE_ORDER = {
[StandardNodeType.Start]: 1,
[StandardNodeType.End]: 2,
@@ -148,7 +148,7 @@ export const NODE_ORDER = {
[StandardNodeType.TriggerDelete]: 28,
};
/** 会话类节点 */
/** session class node */
export const CONVERSATION_NODES = [
StandardNodeType.CreateConversation,
StandardNodeType.UpdateConversation,
@@ -156,7 +156,7 @@ export const CONVERSATION_NODES = [
StandardNodeType.QueryConversationList,
];
/** 消息类节点 */
/** message class node */
export const MESSAGE_NODES = [
StandardNodeType.CreateMessage,
StandardNodeType.UpdateMessage,
@@ -164,7 +164,7 @@ export const MESSAGE_NODES = [
StandardNodeType.QueryMessageList,
];
/** 会话历史类节点 */
/** session history class node */
export const CONVERSATION_HISTORY_NODES = [
StandardNodeType.QueryConversationHistory,
StandardNodeType.ClearContext,

View File

@@ -37,5 +37,5 @@ export interface WorkflowJSON {
edges: WorkflowEdgeJSON[];
}
// 节点模版类型
// Node template type
export { NodeTemplateType } from '@coze-arch/bot-api/workflow_api';

View File

@@ -17,26 +17,26 @@
import { type ViewVariableType } from './view-variable-type';
/**
* 参数定义
* parameter definition
*
* 递归定义,包含了复杂类型的层级结构
* Recursive definition, including a hierarchy of complex types
*/
export interface RecursedParamDefinition {
name?: string;
/** Tree 组件要求每一个节点都有 key而 key 不适合用名称(前后缀)等任何方式赋值,最终确定由接口转换层一次性提供随机 key */
/** The Tree component requires each node to have a key, and the key is not suitable for assignment in any way such as name (before and after). Finally, the interface conversion layer provides a random key at one time. */
fieldRandomKey?: string;
desc?: string;
required?: boolean;
type: ViewVariableType;
children?: RecursedParamDefinition[];
// region 参数值定义
// 输入参数的值可以来自上游变量引用,也可以是用户输入的定值(复杂类型则只允许引用)
// 如果是定值,传 fixedValue
// 如果是引用,传 quotedValue
// Region parameter value definition
// The value of the input parameter can come from an upstream variable reference, or it can be a fixed value entered by the user (for complex types, only references are allowed).
// If it is a fixed value, pass the fixedValue.
// If it is a reference, pass the quotedValue.
isQuote?: ParamValueType;
/** 参数定值 */
/** parameter setting */
fixedValue?: string;
/** 参数引用 */
/** parameter reference */
quotedValue?: [nodeId: string, ...path: string[]]; // string[]
// endregion
}

View File

@@ -34,11 +34,11 @@ import { type StandardNodeType } from './node-type';
export interface WorkflowNodeVariablesMeta {
/**
* 输出变量路径, 默认 ['outputs']
* Output variable path, default ['outputs']
*/
outputsPathList?: string[];
/**
* 输入变量路径,默认 ['inputs.inputParameters']
* Input variable path, default ['input.inputParameters']
*/
inputsPathList?: string[];
batchInputListPath?: string;
@@ -56,7 +56,7 @@ export interface NodeMeta<NodeTest = any> {
isStart?: boolean;
isNodeEnd?: boolean;
deleteDisable?: boolean;
copyDisable?: boolean; // 无法复制
copyDisable?: boolean; // Unable to copy
nodeDTOType: StandardNodeType;
nodeMetaPath?: string;
batchPath?: string;
@@ -65,41 +65,41 @@ export interface NodeMeta<NodeTest = any> {
renderKey?: string;
style?: any;
errorStyle?: any;
size?: { width: number; height: number }; // 初始化时候的默认大小
size?: { width: number; height: number }; // Default size at initialization
defaultPorts?: Array<any>;
useDynamicPort?: boolean;
disableSideSheet?: boolean;
subCanvas?: (node: WorkflowNodeEntity) => WorkflowSubCanvas | undefined;
/** 是否展示触发器 icon */
/** Whether to show trigger icon */
showTrigger?: (props: { projectId?: string }) => boolean;
/** 是否隐藏测试 */
/** Whether to hide the test */
hideTest?: boolean;
/** 获取卡片输入变量标签 */
/** Get card input variable label */
getInputVariableTag?: (
/** 变量名称 */
/** variable name */
variableName: string | undefined,
/** 输入值或变量 */
/** Input value or variable */
input: ValueExpression,
/** 附加参数 */
/** additional parameters */
extra?: {
/**
* 变量服务,从 '@coze-workflow/variable' 导入的 WorkflowVariableService
* 这里不写上类型,是为了避免循环引用,另外 base 包引用 variable 包内容也不合理
* Variable service, WorkflowVariableService imported from '@code-workflow/variable'
* The type is not written here to avoid circular references. In addition, it is unreasonable for the base package to refer to the content of the variable package.
*/
variableService: any;
/** 当前节点 */
/** current node */
node: WorkflowNodeEntity;
},
) => VariableTagProps;
/** 是否支持单节点调试copilot生成表单参数 */
/** Whether to support single-node debugging copilot generation form parameters */
enableCopilotGenerateTestNodeForm?: boolean;
/**
* 获取llm ids,所有需要用到大模型选择的的节点必须实现该方法
* 因为用户可能存在账户升级或者模型下架正常拉到的列表数据可能没有该id
* 所以模型列表获取需要遍历所有节点拿到被用过的模型id
* Get llm ids, all nodes that require large model selection must implement this method
* Because the user may have an account upgrade or the model is removed from the shelves, the list data pulled normally may not have this id.
* Therefore, to obtain the model list, you need to traverse all nodes to get the used model IDs.
* @param nodeJSON
* @returns
*/
@@ -107,17 +107,17 @@ export interface NodeMeta<NodeTest = any> {
nodeJSON: WorkflowNodeJSON,
) => string[] | undefined | string | number | number[];
/**
* 节点 header 不可编辑 & 隐藏 ... 按钮
* Node header not editable & hide... button
*/
headerReadonly?: boolean;
headerReadonlyAllowDeleteOperation?: boolean;
/**
* 节点帮助文档
* Node Help Documentation
*/
helpLink?: string | ((props: { apiName: string }) => string);
/**
* 节点测试相关数据
* Node test related data
*/
test?: NodeTest;
}
@@ -128,14 +128,14 @@ export interface WorkflowNodeRegistry<NodeTestMeta = any>
meta: NodeMeta<NodeTestMeta>;
/**
* 特化:根据节点获取输入 Parameters 的值,默认通过 /inputParameters 字段判断
* Specialization: Get the value of the input Parameters according to the node, and judge by /inputParameters field by default
* @param node
* @returns
*/
getNodeInputParameters?: (node: FlowNodeEntity) => InputValueVO[] | undefined;
/**
* 获取舞台节点右侧更多菜单的额外操作,目前在子流程节点和插件节点中使用
* Get additional actions for the More menu to the right of the Stage node, currently used in the Subprocess node and the Plugin node
* @returns
*/
getHeaderExtraOperation?: (
@@ -144,39 +144,39 @@ export interface WorkflowNodeRegistry<NodeTestMeta = any>
) => React.ReactNode;
/**
* 特化:根据节点获取输出 Outputs 的值,默认通过 /outputs 字段判断
* Specialization: Get the value of Outputs according to the node, and judge by /outputs field by default
*/
getNodeOutputs?: (node: FlowNodeEntity) => OutputValueVO[] | undefined;
/**
* 节点提交前最后一次转化,这个方法在 `formMeta.transformOnSubmit` 之后执行
* @param node 节点数据
* @param 转化后的节点数据
* The last conversion before the node commits, this method is executed after'formMeta.transformOnSubmit'
* @param node data
* @Param converted node data
*/
beforeNodeSubmit?: (node: WorkflowNodeJSON) => WorkflowNodeJSON;
/**
* - 节点 Registry 初始化,可以在这里获取初始化数据,然后再进行表单渲染,注意此时 node 还没有创建
* - 目前适用于 v2 表单引擎节点
* @param nodeJSON 节点初始化数据(点击添加时,或者从后端获取时的节点数据)
* @param context WorkflowPlaygroundContext,参见 packages/workflow/playground/src/workflow-playground-context.ts
* - Node Registry initialization, you can get the initialization data here, and then render the form. Note that the node has not been created at this time
* - Currently works with v2 form engine nodes
* @Param nodeJSON node initialization data (when clicking Add, or when fetching from the backend)
* @Param context WorkflowPlaygroundContext, see packages/workflow/playground/src/workflow-playground-context
* @returns Promise<void>
*/
onInit?: (nodeJSON: WorkflowNodeJSON, context: any) => Promise<void>;
/**
* - 是否有错误信息(从 service 中拿),如果有错误信息返回错误信息,否则返回空串或者 undefined
* - 目前在 node-context-provider.tsx 中消费,监听当前节点错误信息,如果有,更新 FlowNodeErrorData 数据,从而触发节点渲染错误状态
* - 目前适用于 v2 表单引擎节点
* @param nodeJSON 节点初始化数据(点击添加时,或者从后端获取时的节点数据)
* @param context WorkflowPlaygroundContext,参见 packages/workflow/playground/src/workflow-playground-context.ts
* - Whether there is an error message (taken from the service), if there is an error message, return an error message, otherwise return an empty string or undefined
* - Currently consumed in node-context-provider, monitor the current node error message, if any, update the FlowNodeErrorData data, thereby triggering the node to render the error state
* - Currently works with v2 form engine nodes
* @Param nodeJSON node initialization data (when clicking Add, or when fetching from the backend)
* @Param context WorkflowPlaygroundContext, see packages/workflow/playground/src/workflow-playground-context
*/
checkError?: (nodeJSON: WorkflowNodeJSON, context: any) => string | undefined;
/**
* - 节点销毁时调用,用于做一些清理工作,例如将相关节点的错误信息清空,回收资源
* - 目前适用于 v2 表单引擎节点
* @param node 节点
* - Called when the node is destroyed, used to do some cleaning work, such as clearing the error information of the joint point and recycling resources
* - Currently works with v2 form engine nodes
* @param node
* @param context workflow-playground-context
* @returns
*/

View File

@@ -23,11 +23,11 @@ export interface ViewVariableTreeNode {
children?: ViewVariableTreeNode[];
required?: boolean;
description?: string;
// 标识该条参数是内置参数,默认为 false
// Identifies that the parameter is a built-in parameter, and the default is false.
isPreset?: boolean;
// 标识该条参数是否启用,默认为 false
// Identifies whether the parameter is enabled, the default is false
enabled?: boolean;
// 用户自定义节点名展示
// user-defined node name display
label?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
defaultValue?: any;
@@ -37,13 +37,13 @@ export interface ViewVariableMeta extends ViewVariableTreeNode {
required?: boolean;
description?: string;
readonly?: boolean;
mutable?: boolean; // 是否可以被 Set 节点的左值选中
mutable?: boolean; // Can be selected by the lvalue of the Set node
}
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace ViewVariableTreeNode {
/**
* 通过path 查询 子节点
* Query sub-node via path
* @param node
*/
export function getVariableTreeNodeByPath(

View File

@@ -17,7 +17,7 @@
import { type ReactNode } from 'react';
/**
* 前端变量类型
* Front-end variable type
*/
export enum ViewVariableType {
String = 1,
@@ -38,7 +38,7 @@ export enum ViewVariableType {
Svg,
Voice,
Time,
// 上面是 api 中定义的 InputType。下面是整合后的。从 99 开始,避免和后端定义撞车
// The above is the InputType defined in the api. The following is the integrated one. Start from 99 to avoid collisions with the backend definition.
ArrayString = 99,
ArrayInteger,
ArrayBoolean,
@@ -59,7 +59,7 @@ export enum ViewVariableType {
ArrayTime,
}
/**
* 使用 JSON 表示值的变量类型
* Variable types that use JSON to represent values
*/
export const JSON_INPUT_TYPES = [
ViewVariableType.Object,
@@ -99,7 +99,7 @@ export const FILE_TYPES = [
ViewVariableType.ArrayVoice,
];
// 基础类型,及其对应数组类型的配对
// Base types, and pairing with their corresponding array types
export const BASE_ARRAY_PAIR: [ViewVariableType, ViewVariableType][] = [
[ViewVariableType.String, ViewVariableType.ArrayString],
[ViewVariableType.Integer, ViewVariableType.ArrayInteger],
@@ -170,8 +170,8 @@ export namespace ViewVariableType {
}
/**
* 获取所有变量类型的补集
* 该函数由GPT生成
* Get the complement of all variable types
* This function is generated by GPT
* @param inputTypes
*/
export function getComplement(inputTypes: ViewVariableType[]) {
@@ -203,7 +203,7 @@ export namespace ViewVariableType {
}
/**
* 使用 JSON 表示值的变量类型
* Variable types that use JSON to represent values
* @param type
* @returns
*/
@@ -244,30 +244,30 @@ export namespace ViewVariableType {
export interface InputVariable {
name: string;
/**
* id 不一定有,非前后端约定,前端强加上的。风险:workflowSchema 结构化时可能会删掉此属性
* 如需 id ,要在 nodes schema 中定义
* The id does not necessarily have a front-end and back-end convention, imposed by the front-end. Risk: This property may be deleted when workflowSchema is structured
* If you need an id, define it in the nodes schema:
* getDefaultAppendValue: () => ({
id: nanoid(),
}),
* 参考 packages/workflow/nodes/src/workflow-nodes/image-canvas/index.ts
* 只能保证同一节点内唯一,只能保证同一节点内唯一,只能保证同一节点内唯一
* 因为节点可以创建副本
* Reference packages/workflow/nodes/src/workflow-nodes/image-canvas/index.ts
* It can only be guaranteed to be unique within the same node, it can only be guaranteed to be unique within the same node, and it can only be guaranteed to be unique within the same node.
* Because nodes can create replicas
*/
id?: string;
type: ViewVariableType;
/**
* 索引
* index
*/
index: number;
}
/** 卡片上的变量展示标签 */
/** Variable display labels on cards */
export interface VariableTagProps {
key?: string;
/* 变量类型 */
/* Variable type */
type?: ViewVariableType;
/* 变量名,为空时会展示为 Undefined/未定义 */
/* Variable name, when empty, is displayed as Undefined/Undefined */
label?: ReactNode;
/** 是否有效 */
/** Is it valid? */
invalid?: boolean;
}

View File

@@ -15,7 +15,7 @@
*/
/**
* 这个文件定义的是前端消费的数据, 用 VO (view object) 来表示
* This file defines the data consumed by the front end and is represented by VO (view object)
*/
import { isNil, isUndefined } from 'lodash-es';
@@ -23,13 +23,13 @@ import type { ViewVariableType } from './view-variable-type';
export interface RefExpressionContent {
/**
* 引用值是一个变量的 key 路径
* The reference value is the key path of a variable
*/
keyPath: string[];
}
/**
* 值表达式类型
* value expression type
*/
export enum ValueExpressionType {
LITERAL = 'literal',
@@ -37,7 +37,7 @@ export enum ValueExpressionType {
OBJECT_REF = 'object_ref',
}
/**
* 用来存储 ValueExpression 的原始数据供前端消费
* Used to store ValueExpression's original data source for front-end consumption
*/
export interface ValueExpressionRawMeta {
type?: ViewVariableType;
@@ -46,7 +46,7 @@ export interface ValueExpressionRawMeta {
}
/**
* 文本表达式
* Text expression
*/
export interface LiteralExpression {
type: ValueExpressionType.LITERAL;
@@ -55,7 +55,7 @@ export interface LiteralExpression {
}
/**
* 对象表达式
* object expression
*/
export interface ObjectRefExpression {
type: ValueExpressionType.OBJECT_REF;
@@ -64,20 +64,20 @@ export interface ObjectRefExpression {
}
/**
* 引用变量
* reference variable
*/
export interface RefExpression {
type: ValueExpressionType.REF;
content?: RefExpressionContent;
/**
* rawMeta 记录了该 ref expression 的类型
* 可能和所引用变量类型不同,此时会触发类型自动转换: [Workflow 类型自动转换]()
* rawMeta records the type of the ref expression
* May be different from the referenced variable type, which triggers automatic type conversion: [Workflow type automatic conversion] ()
*/
rawMeta?: ValueExpressionRawMeta;
}
/**
* 前端输入值表达式
* Front-end input value expression
*/
export type ValueExpression =
| RefExpression
@@ -116,12 +116,12 @@ export namespace ValueExpression {
return true;
}
// 如果 value 不是对象或者函数,也就是原生类型,在插件自定义组件中会存在
// If value is not an object or function, that is, a native type, it will exist in the plug-in custom component
if (typeof value !== 'object' && typeof value !== 'function') {
return isNil(value);
}
// value.content 有多种类型,可能是 false
// Value.content has multiple types and may be false
if (value?.content === '' || isNil(value?.content)) {
return true;
}
@@ -138,7 +138,7 @@ export namespace ValueExpression {
}
/**
* 前端的value 输入值
* Front end value input value
* {
* name: '',
* input: {

View File

@@ -22,10 +22,10 @@ export const concatTestId = (...testIds: string[]) =>
testIds.filter(id => !!id).join('.');
/**
* 生成节点的测试id
* Generate the test ID of the node.
* @example concatNodeTestId(node, 'right-panel') => playground.node.100001.right-panel
* @param node 节点
* @param testIds 其它id
* @param node
* @param testIds other id
* @returns
*/
export const concatNodeTestId = (node: FlowNodeEntity, ...testIds: string[]) =>

View File

@@ -20,7 +20,7 @@ import { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
import { type FlowNodeEntity } from '@flowgram-adapter/free-layout-editor';
/**
* 找到以 pathEnds 为结尾的 FormItem并获取它的值
* Find the FormItem ending in pathEnds and get its value
* @param node
* @param pathEnds
* @returns
@@ -76,12 +76,12 @@ const findValueByPathEnds = <T = unknown>(
return undefined;
}
// 检查当前路径是否以 pathEnds 结尾
// Check if the current path ends with pathEnds
if (currentPath.endsWith(pathEnds)) {
return obj as T;
}
// 处理对象
// processing object
if (typeof obj === 'object' && !Array.isArray(obj)) {
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
@@ -91,7 +91,7 @@ const findValueByPathEnds = <T = unknown>(
return obj[key] as T;
}
// 递归查找子对象
// recursive search for child objects
const result = findValueByPathEnds(obj[key], pathEnds, newPath);
if (result !== undefined) {
return result as T;
@@ -100,7 +100,7 @@ const findValueByPathEnds = <T = unknown>(
}
}
// 处理数组
// Processing Array
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
const newPath = `${currentPath}/${i}`;
@@ -109,7 +109,7 @@ const findValueByPathEnds = <T = unknown>(
return obj[i] as T;
}
// 递归查找数组元素
// Recursive lookup of array elements
const result = findValueByPathEnds(obj[i], pathEnds, newPath);
if (result !== undefined) {
return result as T;

View File

@@ -18,7 +18,7 @@ import { WorkflowMode } from '@coze-arch/bot-api/workflow_api';
/**
*
* @param flowMode 是否广义上的 workflow包含原来的 Workflow 和 Coze 2.0 新增的 Chatflow
* @Param flowMode Whether it is a workflow in a broad sense, including the original Workflow and the Chatflow added by Coze 2.0
* @returns
*/
export const isGeneralWorkflow = (flowMode: WorkflowMode) =>

View File

@@ -91,14 +91,14 @@ const parseData = (
nodeSchema?.type === StandardNodeType.Text &&
isString(rawOutput) &&
rawOutput?.length > 0;
// 文本节点的 raw_out 不需要反序列化,一定是 stringbadcase:用户拼接的 json 字符串如 '{}'、'123',反序列化后会变成object number
// The raw_out of the text node does not need to be deserialized, it must be a string, badcase: the json string spliced by the user such as '{}'、' 123 'will become object and number after deserialization
const rawOutputData = textHasRawout
? rawOutput?.toString?.()
: rawOutput
? typeSafeJSONParse(rawOutput) || rawOutput?.toString?.()
: undefined;
/** CodeLlm 节点需要展示 raw */
/** Code, Llm nodes need to display raw */
const hasRawOutput =
(Boolean(nodeSchema?.type) &&
[
@@ -107,7 +107,7 @@ const parseData = (
StandardNodeType.Question,
].includes(nodeSchema?.type as StandardNodeType)) ||
textHasRawout;
// StartInput 节点只展示输入
// Start and Input nodes only display input
const hasOutput =
nodeSchema?.type !== StandardNodeType.Start &&
nodeSchema?.type !== StandardNodeType.Input;
@@ -151,7 +151,7 @@ const parseData = (
return {
dataList,
imgList: parseImagesFromOutputData({
// batch data 的 output 下钻了一层,需要再包一层和 output 的 schema 保持一致
// The output of batch data is drilled down one layer, and another layer needs to be wrapped to be consistent with the schema of the output.
outputData: isBatch
? {
outputList: [typeSafeJSONParse(outputJsonString)].filter(Boolean),

View File

@@ -69,10 +69,10 @@ function getImgList(data: unknown, schema: Schema): string[] {
}
/**
* 从节点 output data 中解析图片链接
* @param outputData 节点输出数据 JSON 序列化后的字符串
* @param nodeSchema 节点 schema
* @param excludeNodeTypes 不解析该类型节点的图片链接
* Parse image links from node output data
* @Param outputData node output data JSON serialized string
* @param nodeSchema
* @Param excludeNodeTypes does not resolve image links for nodes of this type
*/
export function parseImagesFromOutputData({
outputData,

View File

@@ -69,10 +69,10 @@ it('extract schema with dataset param parser', () => {
],
});
const extractedSchema = schemaExtractor.extract({
// knowledge 知识库节点 6
// Knowledge Base Node 6
[StandardNodeType.Dataset]: [
{
// 对应知识库名称
// Corresponding knowledge base name
name: 'datasetParam',
path: 'inputs.datasetParam',
parser: SchemaExtractorParserName.DATASET_PARAM,

View File

@@ -41,10 +41,10 @@ it('extract schema with default parser', () => {
],
});
const extractedSchema = schemaExtractor.extract({
// end 结束节点 2
// End End Node 2
[StandardNodeType.End]: [
{
// 对应输出指定内容
// Corresponding output specified content
name: 'content',
path: 'inputs.content.value.content',
},

View File

@@ -73,10 +73,10 @@ it('extract schema with inputParameters parser', () => {
],
});
const extractedSchema = schemaExtractor.extract({
// llm 大模型节点 3
// LLM Large Model Node 3
[StandardNodeType.LLM]: [
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,

View File

@@ -94,10 +94,10 @@ it('extract schema with intents param parser', () => {
],
});
const extractedSchema = schemaExtractor.extract({
// end 结束节点 2
// End End Node 2
[StandardNodeType.Intent]: [
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,

View File

@@ -38,10 +38,10 @@ it('extract schema with json string parser', () => {
],
});
const extractedSchema = schemaExtractor.extract({
// end 结束节点 2
// End End Node 2
[StandardNodeType.SceneChat]: [
{
// 对应输出指定内容
// Corresponding output specified content
name: 'messages',
path: 'inputs.Messages',
parser: SchemaExtractorParserName.JSON_STRING_PARSER,

View File

@@ -61,10 +61,10 @@ it('extract schema with outputs parser', () => {
],
});
const extractedSchema = schemaExtractor.extract({
// code 代码节点 5
// Code Code Node 5
[StandardNodeType.Code]: [
{
// 对应output name
// Corresponding output name
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,

View File

@@ -125,7 +125,7 @@ it('extract schema with variableAssign parser', () => {
const extractedSchema = schemaExtractor.extract({
[StandardNodeType.SetVariable]: [
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.VARIABLE_ASSIGN,
@@ -165,7 +165,7 @@ it('variableAssign parser with empty inputParameters', () => {
const extractedSchema = schemaExtractor.extract({
[StandardNodeType.SetVariable]: [
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.VARIABLE_ASSIGN,
@@ -253,7 +253,7 @@ it('variableAssign parser with invalid schema', () => {
const extractedSchema = schemaExtractor.extract({
[StandardNodeType.SetVariable]: [
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.VARIABLE_ASSIGN,

View File

@@ -20,10 +20,10 @@ import { SchemaExtractorParserName } from '../../constant';
import { StandardNodeType } from '../../../../types';
export const imageflowExtractorConfig: SchemaExtractorConfig = {
// api 节点 4
// API Node 4
[StandardNodeType.Api]: [
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,

View File

@@ -19,211 +19,211 @@ import { SchemaExtractorParserName } from '../../constant';
import { StandardNodeType } from '../../../../types';
export const workflowExtractorConfig: SchemaExtractorConfig = {
// Start 开始节点 1
// Start Start Node 1
[StandardNodeType.Start]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应 input name / description
// Corresponding input name/description
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,
},
],
// End 结束节点 2
// End End Node 2
[StandardNodeType.End]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应输出指定内容
// Corresponding output specified content
name: 'content',
path: 'inputs.content.value.content',
},
],
// LLM 大模型节点 3
// LLM Large Model Node 3
[StandardNodeType.LLM]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应batch value / batch description
// Corresponding batch value/batch description
name: 'batch',
path: 'inputs.batch.inputLists',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应提示词
// Cue word
name: 'llmParam',
path: 'inputs.llmParam',
parser: SchemaExtractorParserName.LLM_PARAM,
},
{
// 对应output name
// Corresponding output name
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,
},
],
// Plugin 节点 4
// Plugin Node 4
[StandardNodeType.Api]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应batch value / batch description
// Corresponding batch value/batch description
name: 'batch',
path: 'inputs.batch.inputLists',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应input value / input description
// Corresponding input value/input description
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应output name
// Corresponding output name
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,
},
],
// Code 代码节点 5
// Code Node 5
[StandardNodeType.Code]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input value / input description
// Corresponding input value/input description
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应code内容
// Corresponding code content
name: 'code',
path: 'inputs.code',
},
{
// 对应output name
// Corresponding output name
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,
},
],
// Knowledge 知识库节点 6
// Knowledge Base Node 6
[StandardNodeType.Dataset]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应知识库名称
// Corresponding knowledge base name
name: 'datasetParam',
path: 'inputs.datasetParam',
parser: SchemaExtractorParserName.DATASET_PARAM,
},
],
// If 判断节点 8
// If the decision node 8
[StandardNodeType.If]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'branches',
path: 'inputs.branches',
parser: SchemaExtractorParserName.DEFAULT,
},
],
// Sub Workflow 工作流节点 9
// Sub Workflow Node 9
[StandardNodeType.SubWorkflow]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应batch value / batch description
// Corresponding batch value/batch description
name: 'batch',
path: 'inputs.batch.inputLists',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应input value / input description
// Corresponding input value/input description
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应output name
// Corresponding output name
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,
},
],
// Variable 变量节点 11
// Variable Node 11
[StandardNodeType.Variable]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应output name
// Corresponding output name
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,
},
],
// Database 数据库节点 12
// Database Node 12
[StandardNodeType.Database]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
@@ -234,21 +234,21 @@ export const workflowExtractorConfig: SchemaExtractorConfig = {
path: 'inputs.sql',
},
{
// 对应output name
// Corresponding output name
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,
},
],
// Message 消息节点 13
// Message Node 13
[StandardNodeType.Output]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
@@ -259,78 +259,78 @@ export const workflowExtractorConfig: SchemaExtractorConfig = {
path: 'inputs.content.value.content',
},
],
// Sub Imageflow 图像流节点 14
// Sub ImageFlow Node 14
[StandardNodeType.Imageflow]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应batch value / batch description
// Corresponding batch value/batch description
name: 'batch',
path: 'inputs.batch.inputLists',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应input value / input description
// Corresponding input value/input description
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应output name
// Corresponding output name
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,
},
],
// Text 文本处理节点 15
// Text processing node 15
[StandardNodeType.Text]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 拼接结果,以及拼接字符串
// Splicing results, and splicing strings
name: 'concatResult',
path: 'inputs.concatParams',
parser: SchemaExtractorParserName.CONCAT_RESULT,
},
{
// 自定义数组拼接符号
// Custom array stitching symbols
name: 'arrayConcatChar',
path: 'inputs.concatParams',
parser: SchemaExtractorParserName.CUSTOM_ARRAY_CONCAT_CHAR,
},
{
// 自定义分隔符
// custom separator
name: 'splitChar',
path: 'inputs.splitParams',
parser: SchemaExtractorParserName.CUSTOM_SPLIT_CHAR,
},
],
// Question 问题节点 18
// Question Node 18
[StandardNodeType.Question]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// question 问题
// Question question
name: 'question',
path: 'inputs.question',
},
{
// answer_type 回答类型 option|text
// answer_type answer type option | text
name: 'answerType',
path: 'inputs.answer_type',
},
@@ -340,69 +340,69 @@ export const workflowExtractorConfig: SchemaExtractorConfig = {
path: 'inputs.options',
},
{
// 对应output name
// Corresponding output name
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,
},
],
// Break 终止循环节点 19
// Break Stop Loop Node 19
[StandardNodeType.Break]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
],
// Set Variable 设置变量节点 20
// Set Variables Set Variables Node 20
[StandardNodeType.SetVariable]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.VARIABLE_ASSIGN,
},
],
// Loop 循环节点 21
// Loop node 21
[StandardNodeType.Loop]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应variable name
// Corresponding variable name
name: 'variables',
path: 'inputs.variableParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应output name
// Corresponding output name
name: 'outputs',
path: 'outputs',
parser: SchemaExtractorParserName.OUTPUTS,
},
],
// Intent 意图识别节点 22
// Intent recognition node 22
[StandardNodeType.Intent]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
@@ -419,21 +419,21 @@ export const workflowExtractorConfig: SchemaExtractorConfig = {
path: 'inputs.llmParam.systemPrompt.value.content',
},
],
// Knowledge Write 知识库写入节点 27
// Knowledge Writing Knowledge Base Writing Node 27
[StandardNodeType.DatasetWrite]: [
{
// 节点自定义名称
// Node custom name
name: 'title',
path: 'nodeMeta.title',
},
{
// 对应input name
// Corresponding input name
name: 'inputs',
path: 'inputs.inputParameters',
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
},
{
// 对应知识库名称
// Corresponding knowledge base name
name: 'datasetParam',
path: 'inputs.datasetParam',
parser: SchemaExtractorParserName.DATASET_PARAM,

View File

@@ -51,10 +51,10 @@ export class SchemaExtractor {
}
public extract(config: SchemaExtractorConfig): SchemaExtracted[] {
this.bindParser(config);
// 1. 遍历schema中node数组对每个node做处理
// 1. Traverse the node array in the schema and process each node
return this.schema.nodes
.map((node: WorkflowNodeJSON): SchemaExtracted | null => {
// 2. 获取节点对应的配置
// 2. Get the configuration corresponding to the node
const nodeConfigs: SchemaExtractorNodeConfig[] = config[node.type];
if (!nodeConfigs) {
return null;
@@ -76,13 +76,13 @@ export class SchemaExtractor {
extractedConfig: Record<string, unknown>,
nodeConfig: SchemaExtractorNodeConfig,
): Record<string, unknown> => {
// 3. 根据节点配置路径获取属性值
// 3. Get the attribute value according to the node configuration path
const rawData: unknown = this.extractProperties(
nodeData,
nodeConfig.path,
);
if (nodeConfig.parser && typeof nodeConfig.parser === 'function') {
// 4. 使用解析器对属性值进行转换
// 4. Use the parser to convert the property value
extractedConfig[nodeConfig.name] = nodeConfig.parser(rawData);
}
return extractedConfig;
@@ -121,7 +121,7 @@ export class SchemaExtractor {
const flattenNodeJSONs: WorkflowNodeJSON[] = [...rootNodes];
const flattenEdgeJSONs: WorkflowEdgeJSON[] = [...rootEdges];
// 如需支持多层结构,以下部分改为递归
// To support multi-layer structures, the following section is changed to recursive
rootNodes.forEach(nodeJSON => {
const { blocks, edges } = nodeJSON;
if (blocks) {

View File

@@ -18,7 +18,7 @@ import { isWorkflowImageTypeURL } from '../utils';
import { type SchemaExtractorOutputsParser } from '../type';
import { AssistTypeDTO, VariableTypeDTO } from '../../../types/dto';
export const outputsParser: SchemaExtractorOutputsParser = outputs => {
// 判断是否为数组
// Determine whether it is an array
if (!Array.isArray(outputs)) {
return [];
}
@@ -31,7 +31,7 @@ export const outputsParser: SchemaExtractorOutputsParser = outputs => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value?: any;
isImage?: boolean;
// 默认值里包含图片时,图片信息单独放到这里
// When the default value includes pictures, the picture information is put here separately.
images?: string[];
} = {
name: output.name || '',
@@ -45,7 +45,7 @@ export const outputsParser: SchemaExtractorOutputsParser = outputs => {
if (output.type === 'list' && Array.isArray(output.schema?.schema)) {
parsed.children = outputsParser(output.schema.schema);
}
// Start 节点默认值放到 value 上
// Start node default value put on value
if (output.defaultValue) {
parsed.value = output.defaultValue;

View File

@@ -57,7 +57,7 @@ export const variableAssignParser: SchemaExtractorVariableAssignParser =
.map(variableAssign => {
const leftContent = getValueExpressionName(variableAssign.left);
const rightContent = getValueExpressionName(variableAssign.right);
// 变量赋值节点的右值字段
// Rvalue field of variable assignment node
const inputContent = variableAssign.input
? getValueExpressionName(variableAssign.input)
: null;

View File

@@ -29,7 +29,7 @@ export type SchemaExtractorConfig = Partial<
export interface SchemaExtractorNodeConfig {
name: string;
/** lodash.get 入参格式 */
/** Lodash.get imported parameter format */
path: string;
parser?: SchemaExtractorParserName | Function;
displayName?: string;
@@ -93,7 +93,7 @@ export type SchemaExtractorOutputsParser = (outputs: VariableMetaDTO[]) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value?: any;
isImage?: boolean;
// 默认值里包含图片时,图片信息单独放到这里
// When the default value includes pictures, the picture information is put here separately.
images?: string[];
}[];

View File

@@ -18,9 +18,9 @@ import { get } from 'lodash-es';
import { type ValueExpressionDTO } from '../../types';
// 是否我们自己上传生成的url, 这是个临时方案,等修复 schema 中的 type:string -> type:image 后,删掉此逻辑
// Whether we upload the generated url ourselves, this is a temporary solution, after fixing the type: string - > type: image in the schema, delete this logic
export function isWorkflowImageTypeURL(str: string): boolean {
// base64 加工
// base64 processing
const hostWhiteList = [
'cC1ib3Qtd29ya2Zsb3ctc2lnbi5ieXRlZGFuY2UubmV0',
'cC1ib3Qtd29ya2Zsb3cuYnl0ZWQub3Jn',

View File

@@ -18,14 +18,14 @@ import { reporter as infraReporter } from '@coze-arch/logger';
const namespace = 'workflow';
/**
* 流程使用的 slardar 上报实例
* The slardar reporting instance used by the process
*/
export const reporter = infraReporter.createReporterWithPreset({
namespace,
});
/**
* 异常捕获,会被当js error上报
* Exception capture will be reported as a js error
* @param exception
* @param importErrorInfo
*/

View File

@@ -16,13 +16,13 @@
import { BOT_USER_INPUT, CONVERSATION_NAME, USER_INPUT } from '../constants';
/**
* 是否预设的开始节点的输入参数
* Whether to preset the input parameters of the start node
*/
export const isPresetStartParams = (name?: string): boolean =>
[BOT_USER_INPUT, USER_INPUT, CONVERSATION_NAME].includes(name ?? '');
/**
* Start 节点参数是 BOT 聊天时用户的输入内容
* The Start node parameter is the user's input during BOT chat
* @param name
* @returns
*/

View File

@@ -38,9 +38,9 @@ export interface TraverseContext {
export type TraverseHandler = (context: TraverseContext) => void;
/**
* 深度遍历对象,对每个值做处理
* @param value 遍历对象
* @param handle 处理函数
* Traverse the object in depth, processing each value
* @param value over object
* @param handling function
*/
export const traverse = <T extends TraverseValue = TraverseValue>(
value: T,
@@ -57,9 +57,9 @@ export const traverse = <T extends TraverseValue = TraverseValue>(
namespace TraverseUtils {
/**
* 深度遍历对象,对每个值做处理
* @param node 遍历节点
* @param handle 处理函数
* Traverse the object in depth, processing each value
* @param node traverse node
* @param handling function
*/
export const traverseNodes = (
node: TraverseNode,
@@ -67,11 +67,11 @@ namespace TraverseUtils {
): void => {
const { value } = node;
if (!value) {
// 异常处理
// exception handling
return;
}
if (Object.prototype.toString.call(value) === '[object Object]') {
// 对象,遍历对象的每个属性
// Object, iterate through each property of the object
Object.entries(value).forEach(([key, item]) =>
traverseNodes(
{
@@ -84,8 +84,8 @@ namespace TraverseUtils {
),
);
} else if (Array.isArray(value)) {
// 数组,遍历数组的每个元素
// 从数组的末尾开始遍历,这样即使中途移除了某个元素,也不会影响到未处理的元素的索引
// Array, iterate through each element of the array
// The iteration starts at the end of the array, so that even if an element is removed halfway through, it will not affect the index of the unprocessed element
for (let index = value.length - 1; index >= 0; index--) {
const item: string = value[index];
traverseNodes(
@@ -117,14 +117,14 @@ namespace TraverseUtils {
});
const setValue = (node: TraverseNode, value: unknown) => {
// 设置值函数
// 引用类型,需要借助父元素修改值
// 由于是递归遍历所以需要根据node来判断是给对象的哪个属性赋值还是给数组的哪个元素赋值
// Set Value Function
// Reference type, you need to modify the value with the help of the parent element
// Since it is a recursive traversal, it is necessary to determine which property of the object to assign a value to, or which element of the array to assign a value to, according to node
if (!value || !node) {
return;
}
node.value = value;
// 从上级作用域node中取出containerkeyindex
// Remove container, key, index from upper scope node
const { container, key, index } = node;
if (key && container) {
container[key] = value;
@@ -162,7 +162,7 @@ namespace TraverseUtils {
if (typeof pathItem === 'string') {
const re = /\W/g;
if (re.test(pathItem)) {
// 包含特殊字符
// Contains special characters
return `${stringifyPath}["${pathItem}"]`;
}
return `${stringifyPath}.${pathItem}`;

View File

@@ -57,7 +57,7 @@ function useSelection(editor: ExpressionEditorAPI | undefined) {
const view = editor.$view;
function updateSelection(update?: ViewUpdate) {
// 忽略 replaceTextByRange 导致的 selection change(效果:不触发 selection 变更,进而不显示推荐面板)
// Ignore the selection change caused by replaceTextByRange (effect: no selection change is triggered, and the recommendation panel is not displayed)
if (update?.transactions.some(tr => isSkipSelectionChangeUserEvent(tr))) {
setSelection(undefined);
return;

View File

@@ -23,7 +23,7 @@ import { type ExpressionEditorTreeNode } from '@/expression-editor';
import { generateUniqueId, getSearchValue, useLatest } from '../../shared';
import { type InterpolationContent } from './types';
// 在数据更新后,强制 Tree 组件重新渲染
// Force the Tree component to re-render after the data update
function useTreeRefresh(filteredVariableTree: ExpressionEditorTreeNode[]) {
const [treeRefreshKey, setTreeRefreshKey] = useState('');
@@ -34,7 +34,7 @@ function useTreeRefresh(filteredVariableTree: ExpressionEditorTreeNode[]) {
return treeRefreshKey;
}
// Tree 组件重新渲染后进行搜索
// Search after the Tree component is re-rendered
// eslint-disable-next-line max-params
function useTreeSearch(
treeRefreshKey: string,

View File

@@ -104,10 +104,10 @@ function Popover({
const { elements } = optionsInfo;
selectNodeByIndex(elements, 0);
});
// selected 仅用于 Tree 组件对应项展示蓝色选中效果,无其他用途
// Selected is only used to display the blue selection effect for the corresponding item of the Tree component, and has no other purpose.
const selected = useSelectedValue(interpolationContent?.text, variableTree);
// 基于用户选中项,替换所在 {{}} 中的内容
// Replace content in {{}} based on user selection
const handleSelect = useCallback(
// eslint-disable-next-line @typescript-eslint/naming-convention
(_, __, node: TreeNodeData) => {
@@ -126,7 +126,7 @@ function Popover({
Boolean(emptyContent));
const [allowVisible, setAllowVisible] = useState(false);
// 选区变化时,清除锁定效果
// Clear the lock effect when the selection changes
useEffect(() => {
setAllowVisible(true);
}, [selection]);
@@ -140,14 +140,14 @@ function Popover({
treeRef,
);
// 上下键切换推荐项,回车填入
// Press the up and down keys to switch the recommended items, and press Enter to fill in.
useKeyboard(visible, {
ArrowUp: prev,
ArrowDown: next,
Enter: apply,
});
// ESC 关闭
// ESC Close
useKeyboard(visible, {
// eslint-disable-next-line @typescript-eslint/naming-convention
Escape() {
@@ -155,7 +155,7 @@ function Popover({
},
});
// 推荐面板出现时,禁用 ArrowUp/ArrowDown/Enter 的默认行为(行为改为上下键切换推荐项 & 回车插入)
// When the recommendation panel appears, disable the default behavior of ArrowUp/ArrowDown/Enter (the behavior is changed to up and down keys to switch recommendations & enter to insert)
useEffect(() => {
if (visible === true) {
editorRef.current?.disableKeybindings(['ArrowUp', 'ArrowDown', 'Enter']);

View File

@@ -43,7 +43,7 @@ const getOptionInfoFromDOM = (
return;
}
// 获取所有的选项元素
// Get all option elements
const foundNodes = root.querySelectorAll(
'.semi-tree-option-list .semi-tree-option',
);
@@ -54,7 +54,7 @@ const getOptionInfoFromDOM = (
const optionElements = [...foundNodes];
// 找到当前高亮的选项
// Find the currently highlighted option
const selectedIndex = optionElements.findIndex(element =>
element.classList.contains(SELECTED_OPTION_CLASSNAME),
);

View File

@@ -77,9 +77,9 @@ function useExtensions(
if (
cursor.node.type.name === 'Interpolation' &&
// 由于 parser 存在容错能力
// 可能出现缺少右花括号也被正常解析为 Interpolation 的情况
// 如:{{variable
// Due to the fault tolerance of the parser
// It is possible that the missing right curly brace is also parsed normally as Interpolation
// Such as: {{variable
cursor.node.firstChild?.type.name === '{{' &&
cursor.node.lastChild?.type.name === '}}'
) {

View File

@@ -70,8 +70,8 @@ function Renderer({
[onChange],
);
// Note: changedVariableTree 这里只用来进行性能优化
// useVariableTree 的触发时机仍然存在问题,缩放画布也会频繁触发 variableTree 的变更
// Note: changedVariableTree is only used for performance optimization
// useVariableTree still has issues with the timing of triggering, and scaling the canvas also frequently triggers variableTree changes
useEffect(() => {
const editor = apiRef.current;
@@ -92,7 +92,7 @@ function Renderer({
editor.updateWholeDecorations();
}
// 值受控
// value controlled
useEffect(() => {
const editor = apiRef.current;

View File

@@ -33,10 +33,10 @@ function useLatest<T>(value: T): MutableRefObject<T> {
return ref;
}
// 解除 parent 导致的循环依赖(否则无法深比较)
// Remove circular dependencies caused by parents (otherwise no deep comparison is possible)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function cloneWithout(target: any, keys: string[]) {
// target undefined 时会抛错
// An error is thrown when target is undefined
try {
return JSON.parse(
JSON.stringify(target, function (key, value) {
@@ -85,7 +85,7 @@ function getSearchValue(textBefore: string) {
const lastSegment =
segments[segments.length - 1].type ===
ExpressionEditorSegmentType.ArrayIndex
? segments[segments.length - 2] // 数组索引属于上一层级,需要去除防止影响到搜索值
? segments[segments.length - 2] // The array index belongs to the previous level and needs to be removed to prevent it from affecting the search value
: segments[segments.length - 1];
if (
!lastSegment ||

View File

@@ -31,7 +31,7 @@ interface ExpressionEditorCounterProps {
}
/**
* 长度计数器
* length counter
*/
export const ExpressionEditorCounter: FC<
ExpressionEditorCounterProps

View File

@@ -36,7 +36,7 @@ interface ExpressionEditorRenderProps {
}
/**
* 应当只包含编辑器逻辑,业务无关
* It should only contain editor logic, business-independent
*/
export const ExpressionEditorRender: React.FC<
ExpressionEditorRenderProps
@@ -57,7 +57,7 @@ export const ExpressionEditorRender: React.FC<
editor={model.editor}
initialValue={model.lines}
onChange={value => {
// eslint-disable-next-line @typescript-eslint/require-await -- 防止阻塞 slate 渲染
// eslint-disable-next-line @typescript-eslint/require-await -- prevent blocking slate rendering
const asyncOnChange = async () => {
const lines = value as ExpressionEditorLine[];
model.change(lines);

View File

@@ -45,9 +45,9 @@ import {
import styles from './index.module.less';
/** 内置函数 */
/** built-in function */
namespace SuggestionViewUtils {
/** 编辑器选中事件处理 */
/** Editor selected event handling */
export const editorSelectHandler = (params: {
reducer: SuggestionReducer;
payload: ExpressionEditorEventParams<ExpressionEditorEvent.Select>;
@@ -55,7 +55,7 @@ namespace SuggestionViewUtils {
const { reducer, payload } = params;
const [state, dispatch] = reducer;
// 设置解析数据
// Set up parse data
const parseData = ExpressionEditorParser.parse({
lineContent: payload.content,
lineOffset: payload.offset,
@@ -83,7 +83,7 @@ namespace SuggestionViewUtils {
},
});
// 重置UI组件内部状态
// Reset UI component internal state
const shouldRefresh = parseData.content.reachable === '';
if (shouldRefresh) {
dispatch({
@@ -91,7 +91,7 @@ namespace SuggestionViewUtils {
});
}
// 设置选中值
// Set the selected value
const selected = SuggestionViewUtils.computeSelected({
model: state.model,
parseData,
@@ -101,7 +101,7 @@ namespace SuggestionViewUtils {
payload: selected,
});
// 设置可见变量树
// Set the visible variable tree
const variableTree = SuggestionViewUtils.computeVariableTree({
model: state.model,
parseData,
@@ -111,7 +111,7 @@ namespace SuggestionViewUtils {
payload: variableTree,
});
// 设置匹配树枝
// Set matching branches
const matchTreeBranch: ExpressionEditorTreeNode[] | undefined =
ExpressionEditorTreeHelper.matchTreeBranch({
tree: state.model.variableTree,
@@ -122,7 +122,7 @@ namespace SuggestionViewUtils {
payload: matchTreeBranch,
});
// 设置空内容
// Set empty content
const emptyContent = SuggestionViewUtils.computeEmptyContent({
parseData,
fullVariableTree: state.model.variableTree,
@@ -134,7 +134,7 @@ namespace SuggestionViewUtils {
payload: emptyContent,
});
// 设置UI相对坐标
// Set UI relative coordinates
const rect = SuggestionViewUtils.computeRect(state);
dispatch({
type: SuggestionActionType.SetRect,
@@ -147,9 +147,9 @@ namespace SuggestionViewUtils {
return;
}
// FIXME: 设置搜索值很hack的逻辑后面建议重构不用semi组件自己写一个
// FIXME: Set the search value, very hacked logic. Later, it is recommended to refactor without semi components, and write one yourself.
if (!state.ref.tree.current) {
// 不设为可见获取不到ref
// Not set to visible can't get ref
dispatch({
type: SuggestionActionType.SetVisible,
payload: true,
@@ -168,7 +168,7 @@ namespace SuggestionViewUtils {
return 1;
};
/** 计算可见时相对父容器坐标 */
/** Calculate relative parent container coordinates when visible */
export const computeRect = (
state: SuggestionState,
):
@@ -194,12 +194,12 @@ namespace SuggestionViewUtils {
left: (rect.left - containerRect.left) / getFinalScale(state),
};
} catch (e) {
// slate DOM 计算报错可忽略
// Slate DOM calculation error can be ignored
return;
}
};
/** 计算当前选中变量 */
/** Calculate the currently selected variable */
export const computeSelected = (params: {
model: ExpressionEditorModel;
parseData: ExpressionEditorParseData;
@@ -218,7 +218,7 @@ namespace SuggestionViewUtils {
return treeBrach[treeBrach.length - 1];
};
/** 计算当前搜索值 */
/** Calculate the current search value */
export const computeSearch = (
parseData: ExpressionEditorParseData,
): string => {
@@ -229,7 +229,7 @@ namespace SuggestionViewUtils {
const lastSegment =
segments[segments.length - 1].type ===
ExpressionEditorSegmentType.ArrayIndex
? segments[segments.length - 2] // 数组索引属于上一层级,需要去除防止影响到搜索值
? segments[segments.length - 2] // The array index belongs to the previous level and needs to be removed to prevent it from affecting the search value
: segments[segments.length - 1];
if (
!lastSegment ||
@@ -240,7 +240,7 @@ namespace SuggestionViewUtils {
return lastSegment.objectKey;
};
/** 计算裁剪层级的变量树 */
/** Calculate the variable tree of the clipping level */
export const computeVariableTree = (params: {
model: ExpressionEditorModel;
parseData: ExpressionEditorParseData;
@@ -293,7 +293,7 @@ namespace SuggestionViewUtils {
export const keyboardSelectedClassName = () =>
styles['expression-editor-suggestion-keyboard-selected'];
/** 将选中项设为高亮 */
/** Highlight the selected item */
export const setUIOptionSelected = (uiOption: Element): void => {
if (
!uiOption?.classList?.add ||
@@ -305,7 +305,7 @@ namespace SuggestionViewUtils {
uiOption.classList.add(SuggestionViewUtils.keyboardSelectedClassName());
};
/** 获取所有选项UI元素 */
/** Get all options UI elements */
export const computeUIOptions = (
state: SuggestionState,
):
@@ -315,14 +315,14 @@ namespace SuggestionViewUtils {
selectedOption?: Element;
}
| undefined => {
// 获取所有的选项元素
// Get all option elements
const optionListDom =
state.ref.suggestion.current?.children?.[0]?.children?.[1]?.children;
if (!optionListDom) {
return;
}
const optionList = Array.from(optionListDom);
// 找到当前高亮的选项
// Find the currently highlighted option
const selectedIndex = optionList.findIndex(element =>
element.classList.contains(keyboardSelectedClassName()),
);
@@ -333,7 +333,7 @@ namespace SuggestionViewUtils {
};
};
/** 禁止变更 visible 防止 ui 抖动 */
/** Disable visible changes Prevent UI jitter */
export const preventVisibleJitter = (
reducer: SuggestionReducer,
time = 150,
@@ -354,18 +354,18 @@ namespace SuggestionViewUtils {
}, time);
};
/** 清空键盘UI选项 */
/** Clear keyboard UI options */
export const clearSelectedUIOption = (state: SuggestionState) => {
const uiOptions = SuggestionViewUtils.computeUIOptions(state);
if (uiOptions?.selectedOption) {
// 清空键盘选中状态
// Clear keyboard selection
uiOptions.selectedOption.classList.remove(
SuggestionViewUtils.keyboardSelectedClassName(),
);
}
};
/** 默认键盘UI选项为第一项 */
/** The default keyboard UI option is the first item */
export const selectFirstUIOption = (state: SuggestionState) => {
const uiOptions = SuggestionViewUtils.computeUIOptions(state);
if (!uiOptions?.optionList) {
@@ -375,12 +375,12 @@ namespace SuggestionViewUtils {
if (!uiOptions?.optionList?.[0]?.classList?.add) {
return;
}
// 默认首项高亮
// Default first item highlighting
SuggestionViewUtils.setUIOptionSelected(uiOptions.optionList[0]);
};
}
/** 选中节点 */
/** selected node */
export const useSelectNode = (reducer: SuggestionReducer) => {
const [state] = reducer;
return useCallback(
@@ -403,7 +403,7 @@ export const useSelectNode = (reducer: SuggestionReducer) => {
},
};
const insertText = `${ExpressionEditorToken.FullStart}${fullPath}${ExpressionEditorToken.FullEnd}`;
// 替换文本
// replacement text
Transforms.insertText(state.model.editor, insertText, {
at: selection,
});
@@ -412,12 +412,12 @@ export const useSelectNode = (reducer: SuggestionReducer) => {
);
};
/** 挂载监听器 */
/** mount the listener */
export const useListeners = (reducer: SuggestionReducer) => {
const [state, dispatch] = reducer;
useEffect(() => {
// 挂载监听: 鼠标点击事件
// Mount Listening: Mouse Click Events
const mouseHandler = (e: MouseEvent) => {
if (!state.visible || !state.ref.suggestion.current) {
return;
@@ -439,13 +439,13 @@ export const useListeners = (reducer: SuggestionReducer) => {
window.removeEventListener('mousedown', mouseHandler);
};
return () => {
// 销毁时卸载监听器防止内存泄露
// Prevent memory leaks by uninstalling listeners when destroyed
mouseDisposer();
};
}, [state]);
useEffect(() => {
// 挂载监听: 编辑器选择事件
// Mount Listening: Editor Selection Event
const editorSelectDisposer = state.model.on<ExpressionEditorEvent.Select>(
ExpressionEditorEvent.Select,
payload =>
@@ -455,13 +455,13 @@ export const useListeners = (reducer: SuggestionReducer) => {
}),
);
return () => {
// 销毁时卸载监听器防止内存泄露
// Prevent memory leaks by uninstalling listeners when destroyed
editorSelectDisposer();
};
}, []);
useEffect(() => {
// 挂载监听: 编辑器拼音输入事件
// Mount Monitor: Editor Pinyin Input Event
const compositionStartDisposer =
state.model.on<ExpressionEditorEvent.CompositionStart>(
ExpressionEditorEvent.CompositionStart,
@@ -472,13 +472,13 @@ export const useListeners = (reducer: SuggestionReducer) => {
}),
);
return () => {
// 销毁时卸载监听器防止内存泄露
// Prevent memory leaks by uninstalling listeners when destroyed
compositionStartDisposer();
};
}, []);
useEffect(() => {
// 初始化前首次渲染激活DOM
// First render activation DOM before initialization
if (state.initialized) {
return;
}
@@ -489,7 +489,7 @@ export const useListeners = (reducer: SuggestionReducer) => {
}, []);
useEffect(() => {
// 初始化前监听到DOM激活后隐藏
// Listen to DOM activation before initialization and hide after activation
if (state.initialized || state.visible) {
return;
}
@@ -503,14 +503,14 @@ export const useListeners = (reducer: SuggestionReducer) => {
}, [state]);
};
/** 键盘上下回车键选中节点 */
/** Keyboard Enter up and down to select the node */
export const useKeyboardSelect = (
reducer: SuggestionReducer,
selectNode: (node: ExpressionEditorTreeNode) => void,
) => {
const [state, dispatch] = reducer;
// 键盘上下
// Keyboard up and down
useEffect(() => {
const keyboardArrowHandler = event => {
if (
@@ -526,34 +526,34 @@ export const useKeyboardSelect = (
}
const { optionList, selectedIndex } = uiOptions;
if (optionList.length === 1) {
// 仅有一项可选项的时候不做处理
// Do not deal with when there is only one option
return;
}
event.preventDefault();
let newIndex = selectedIndex;
if (event.key === 'ArrowDown') {
// 如果当前没有高亮的选项或者是最后一个选项,则高亮第一个选项
// If there is currently no highlighted option or the last option, highlight the first option
newIndex =
selectedIndex === -1 || selectedIndex === optionList.length - 1
? 0
: selectedIndex + 1;
} else if (event.key === 'ArrowUp') {
// 如果当前没有高亮的选项或者是第一个选项,则高亮最后一个选项
// If there is currently no highlighted option or the first option, highlight the last option
newIndex =
selectedIndex <= 0 ? optionList.length - 1 : selectedIndex - 1;
}
const selectedOption = optionList[newIndex];
// 更新高亮选项
// Update highlighting options
if (selectedIndex !== -1) {
optionList[selectedIndex].classList.remove(
SuggestionViewUtils.keyboardSelectedClassName(),
);
}
SuggestionViewUtils.setUIOptionSelected(selectedOption);
// 将新选中的选项滚动到视图中
// Scroll the newly selected option into view
selectedOption.scrollIntoView({
behavior: 'smooth', // 平滑滚动
block: 'nearest', // 最接近的视图边界,可能是顶部或底部
behavior: 'smooth', // Smooth scrolling
block: 'nearest', // The closest view boundary, possibly the top or bottom
});
};
document.addEventListener('keydown', keyboardArrowHandler);
@@ -562,7 +562,7 @@ export const useKeyboardSelect = (
};
}, [state]);
// 键盘回车
// Keyboard Enter
useEffect(() => {
const keyboardEnterHandler = event => {
if (
@@ -596,13 +596,13 @@ export const useKeyboardSelect = (
!variableTreeNode.variable?.children ||
variableTreeNode.variable?.children.length === 0
) {
// 叶子节点
// leaf node
return;
}
// 非叶子节点,光标向前移动两格
// Non-leaf node, move the cursor forward two spaces
const { selection } = state.model.editor;
if (selection && Range.isCollapsed(selection)) {
// 向前移动两个字符的光标
// Move the cursor two characters forward
Transforms.move(state.model.editor, { distance: 2, reverse: true });
}
SuggestionViewUtils.preventVisibleJitter(reducer);
@@ -613,7 +613,7 @@ export const useKeyboardSelect = (
};
}, [state]);
// 键盘 ESC 取消弹窗
// Keyboard ESC cancel pop-up window
useEffect(() => {
const keyboardESCHandler = event => {
if (
@@ -635,17 +635,17 @@ export const useKeyboardSelect = (
};
}, [state]);
// 默认选中首项
// First item selected by default
useEffect(() => {
SuggestionViewUtils.selectFirstUIOption(state);
}, [state]);
};
/** 等待semi组件数据更新后的副作用 */
/** Side effects of waiting for semi component data to be updated */
export const useRenderEffect = (reducer: SuggestionReducer) => {
const [state, dispatch] = reducer;
// 组件树状数据更新后设置搜索值
// Set the search value after the component tree data is updated
useEffect(() => {
if (!state.renderEffect.search || !state.parseData) {
return;
@@ -667,7 +667,7 @@ export const useRenderEffect = (reducer: SuggestionReducer) => {
});
}, [state]);
// 搜索过滤后是否为空
// Is it empty after searching for filters?
useEffect(() => {
if (!state.renderEffect.filtered) {
return;

View File

@@ -45,7 +45,7 @@ interface ExpressionEditorSuggestionProps {
}
/**
* 自动提示
* autoprompt
*/
export const ExpressionEditorSuggestion: FC<
ExpressionEditorSuggestionProps

View File

@@ -85,7 +85,7 @@ export const suggestionReducer = (
action.payload as SuggestionActionPayload<SuggestionActionType.SetVisible>;
if (state.entities.selectorBoxConfig) {
if (visible) {
state.entities.selectorBoxConfig.disabled = true; // 防止鼠标拖选不触发点击
state.entities.selectorBoxConfig.disabled = true; // Prevent mouse dragging from triggering clicks
}
if (!visible) {
state.entities.selectorBoxConfig.disabled = false;
@@ -166,7 +166,7 @@ export const suggestionReducer = (
return state;
};
/** 获取状态 */
/** Get status */
export const useSuggestionReducer = (
initialState: Omit<
SuggestionState,
@@ -182,15 +182,15 @@ export const useSuggestionReducer = (
): SuggestionReducer => {
const [state, dispatch]: SuggestionReducer = useReducer(suggestionReducer, {
...initialState,
initialized: false, // 初始化
version: 0, // 更新状态计数
key: 0, // 用于触发 react 重新渲染组件
variableTree: [], // 用于展示的组件树
visible: true, // 默认显示让ref能访问到DOM
hiddenDOM: true, // 默认隐藏,让用户看不到UI
allowVisibleChange: true, // 允许visible变更
initialized: false, // initialization
version: 0, // update status count
key: 0, // Used to trigger react to re-render components
variableTree: [], // Component tree for presentation
visible: true, // Default display, allowing ref to access the DOM
hiddenDOM: true, // Hidden by default, so that users cannot see the UI.
allowVisibleChange: true, // Allow visible changes
renderEffect: {
// 渲染副作用
// rendering side effects
search: false,
filtered: false,
},

View File

@@ -60,27 +60,27 @@ export class ExpressionEditorModel {
this.innerLines = ExpressionEditorParser.deserialize(initialValue);
}
/** 设置变量树 */
/** Set variable tree */
public setVariableTree(variableTree: ExpressionEditorTreeNode[]): void {
this.innerVariableTree = variableTree;
}
/** 获取变量树 */
/** Get variable tree */
public get variableTree(): ExpressionEditorTreeNode[] {
return this.innerVariableTree;
}
/** 获取行数据 */
/** Get row data */
public get lines(): ExpressionEditorLine[] {
return this.innerLines;
}
/** 获取序列化值 */
/** Get Serialized Value */
public get value(): string {
return this.innerValue;
}
/** 外部设置模型值 */
/** External setting model values */
public setValue(value: string): void {
if (value === this.innerValue) {
return;
@@ -90,22 +90,22 @@ export class ExpressionEditorModel {
this.syncEditorValue();
}
/** 同步选中状态 */
/** Synchronize selected state */
public setFocus(focus: boolean): void {
if (this.innerFocus === focus) {
return;
}
this.innerFocus = focus;
if (focus) {
// 首次选中时主动触发选区事件,主动触发变量推荐
// Active trigger selection event when first selected, active trigger variable recommendation
this.select(this.lines);
} else if (this.innerValue !== '' && this.editor.children.length !== 0) {
// 触发失焦且编辑器内容不为空,则重置选区
// Trigger out of focus and editor content is not empty, reset the selection
Transforms.select(this.editor, Editor.start(this.editor, []));
}
}
/** 注册事件 */
/** Register an event */
public on<T extends ExpressionEditorEvent>(
event: T,
callback: (params: ExpressionEditorEventParams<T>) => void,
@@ -116,7 +116,7 @@ export class ExpressionEditorModel {
};
}
/** 数据变更事件 */
/** data change event */
public change(lines: ExpressionEditorLine[]): void {
const isAstChange = this.editor.operations.some(
op => 'set_selection' !== op.type,
@@ -132,7 +132,7 @@ export class ExpressionEditorModel {
});
}
/** 选中事件 */
/** selected event */
public select(lines: ExpressionEditorLine[]): void {
const { selection } = this.editor;
if (!selection || !Range.isCollapsed(selection)) {
@@ -143,7 +143,7 @@ export class ExpressionEditorModel {
selection.anchor.path[0] !== selection.focus.path[0] ||
selection.anchor.path[1] !== selection.focus.path[1]
) {
// 框选
// box selection
this.emitter.emit(ExpressionEditorEvent.Select, {
content: '',
offset: -1,
@@ -169,7 +169,7 @@ export class ExpressionEditorModel {
});
}
/** 键盘事件 */
/** keyboard event */
public keydown(
event: Parameters<KeyboardEventHandler<HTMLDivElement>>[0],
): void {
@@ -184,7 +184,7 @@ export class ExpressionEditorModel {
reverse: true,
});
setTimeout(() => {
// slate UI 渲染滞后
// Slate UI Rendering
this.select(this.innerLines);
}, 0);
}
@@ -203,7 +203,7 @@ export class ExpressionEditorModel {
}
}
/** 开始输入拼音 */
/** Start typing pinyin */
public compositionStart(
event: CompositionEventHandler<HTMLDivElement>,
): void {
@@ -212,7 +212,7 @@ export class ExpressionEditorModel {
});
}
/** 装饰叶子节点 */
/** Decorative leaf node */
public get decorate(): ([node, path]: NodeEntry) => ExpressionEditorRange[] {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
@@ -221,7 +221,7 @@ export class ExpressionEditorModel {
if (!Text.isText(node)) {
return ranges;
}
// 计算表达式合法/非法
// Evaluation expressions are legal/illegal
const validateList = ExpressionEditorValidator.lineTextValidate({
lineText: node.text,
tree: self.innerVariableTree,
@@ -247,7 +247,7 @@ export class ExpressionEditorModel {
if (!this.innerFocus) {
return ranges;
}
// 以下是计算当前选中表达式逻辑
// The following is the logic for evaluating the currently selected expression
const selectedItem = self.isValidateSelectPath([node, path]);
const selectedValidItem = validateList.find(
validateData =>
@@ -274,17 +274,17 @@ export class ExpressionEditorModel {
}
/**
* 同步编辑器实例内容
* > **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 {
// 删除编辑器内所有行
// Delete all lines in the editor
this.editor.children.forEach((line, index) => {
Transforms.removeNodes(this.editor, {
at: [index],
});
});
// 重新在编辑器插入当前行内容
// Reinsert the current line content in the editor
this.lines.forEach((line, index) => {
Transforms.insertNodes(this.editor, line, {
at: [this.editor.children.length],

View File

@@ -29,7 +29,7 @@ import {
} from '../constant';
export namespace ExpressionEditorParserBuiltin {
/** 计算开始和结束标识的序号 */
/** Calculate the serial number of the start and end tags */
export const tokenOffset = (line: {
lineContent: string;
lineOffset: number;
@@ -52,7 +52,7 @@ export namespace ExpressionEditorParserBuiltin {
firstEndTokenOffset + 2,
);
if (endChars !== ExpressionEditorToken.FullEnd) {
// 结束符号 "}}" 不完整
// End symbol "}}" is incomplete
return;
}
const lastStartTokenOffset = content.lastIndexOf(
@@ -64,7 +64,7 @@ export namespace ExpressionEditorParserBuiltin {
lastStartTokenOffset + 1,
);
if (startChars !== ExpressionEditorToken.FullStart) {
// 开始符号 "{{" 不完整
// The opening symbol "{{" is incomplete
return;
}
return {
@@ -73,7 +73,7 @@ export namespace ExpressionEditorParserBuiltin {
};
};
/** 从行内容提取内容 */
/** Extract content from line content */
export const extractContent = (params: {
lineContent: string;
lineOffset: number;
@@ -102,7 +102,7 @@ export namespace ExpressionEditorParserBuiltin {
};
};
/** 根据 offset 将文本内容切分为可用与不可用 */
/** Split text content into available and unavailable by offset */
export const sliceReachable = (params: {
content: string;
offset: number;
@@ -119,29 +119,29 @@ export namespace ExpressionEditorParserBuiltin {
};
};
/** 切分文本 */
/** Split text */
export const splitText = (pathString: string): string[] => {
// 得到的分割数组,初始为原字符串以"."分割的结果
// The obtained split array, initially the result of splitting the original string with "."
const segments = pathString.split(ExpressionEditorToken.Separator);
// 定义结果数组,并处理连续的"."导致的空字符串
// Define the result array and handle empty strings resulting from consecutive "."
const result: string[] = [];
segments.forEach(segment => {
if (!segment.match(/\[\d+\]/)) {
// 如果不是数组索引,直接加入结果数组,即使是空字符串也加入以保持正确的分割
// If it is not an array index, add the result array directly, even if it is an empty string, to maintain the correct segmentation
result.push(segment);
return;
}
// 如果当前段是数组索引,将前面的字符串和当前数组索引分别加入结果数组
// If the current segment is an array index, add the previous string and the current array index to the result array, respectively
const lastSegmentIndex = segment.lastIndexOf(
ExpressionEditorToken.ArrayStart,
);
const key = segment.substring(0, lastSegmentIndex);
const index = segment.substring(lastSegmentIndex);
// {{array[0]}} 中的 array
// Array in {{array [0]}}
result.push(key);
// {{array[0]}} 中的 [0]
// [0] in {{array [0]}}
result.push(index);
return;
});
@@ -149,14 +149,14 @@ export namespace ExpressionEditorParserBuiltin {
return result;
};
/** 字符串解析为路径 */
/** String parsed as path */
export const toSegments = (
text: string,
): ExpressionEditorSegment[] | undefined => {
const textSegments = ExpressionEditorParserBuiltin.splitText(text);
const segments: ExpressionEditorSegment[] = [];
const validate = textSegments.every((textSegment, index) => {
// 数组下标
// array subscript
if (
textSegment.startsWith(ExpressionEditorToken.ArrayStart) &&
textSegment.endsWith(ExpressionEditorToken.ArrayEnd)
@@ -164,7 +164,7 @@ export namespace ExpressionEditorParserBuiltin {
const arrayIndexString = textSegment.slice(1, -1);
const arrayIndex = Number(arrayIndexString);
if (arrayIndexString === '' || Number.isNaN(arrayIndex)) {
// index 必须是数字
// Index must be a number
return false;
}
const lastSegment = segments[segments.length - 1];
@@ -172,7 +172,7 @@ export namespace ExpressionEditorParserBuiltin {
!lastSegment ||
lastSegment.type !== ExpressionEditorSegmentType.ObjectKey
) {
// 数组索引必须在 key 之后
// The array index must be after the key
return false;
}
segments.push({
@@ -181,7 +181,7 @@ export namespace ExpressionEditorParserBuiltin {
arrayIndex,
});
}
// 最后一行空文本
// The last empty line of text
else if (index === textSegments.length - 1 && textSegment === '') {
segments.push({
type: ExpressionEditorSegmentType.EndEmpty,
@@ -207,11 +207,11 @@ export namespace ExpressionEditorParserBuiltin {
}
export namespace ExpressionEditorParser {
/** 序列化 */
/** Serialization */
export const serialize = (value: ExpressionEditorLine[]) =>
value.map(n => Node.string(n)).join('\n');
/** 反序列化 */
/** deserialization */
export const deserialize = (text: string): ExpressionEditorLine[] => {
const lines = text.split('\n');
return lines.map(line => ({

View File

@@ -165,7 +165,7 @@ export namespace ExpressionEditorTreeHelper {
const lastSegment = segments[segments.length - 1];
const segmentsRemovedLast =
lastSegment.type === ExpressionEditorSegmentType.ArrayIndex
? segments.slice(0, segments.length - 2) // 数组索引属于上一层级,需要去除两层
? segments.slice(0, segments.length - 2) // The array index belongs to the previous level, and two layers need to be removed.
: segments.slice(0, segments.length - 1);
let treeLayer = tree;
segmentsRemovedLast.forEach(segment => {
@@ -193,7 +193,7 @@ export namespace ExpressionEditorTreeHelper {
const pathList: { objectKey: string; arrayIndex?: number }[] = [];
while (current) {
if (current.variable?.type === ViewVariableType.ArrayObject) {
// 默认第0个
// Default 0th
pathList.unshift({
objectKey: current.label,
arrayIndex: 0,
@@ -213,7 +213,7 @@ export namespace ExpressionEditorTreeHelper {
const pathItem = pathList[pathIndex];
pathIndex++;
if (pathItem.objectKey !== segment.objectKey) {
// 退出循环
// exit the loop
return true;
}
const nextSegment = segments[index + 1];
@@ -253,7 +253,7 @@ export namespace ExpressionEditorTreeHelper {
return false;
};
const beforeTreeNode = treeBranch[treeBranch.length - 1];
// 确认非法情况:是否对非数组类型使用数组索引
// Verify Illegal Case: Whether to Use Array Indexing for Non-Array Types
if (
segment.type === ExpressionEditorSegmentType.ArrayIndex &&
beforeTreeNode &&
@@ -262,7 +262,7 @@ export namespace ExpressionEditorTreeHelper {
) {
return itemInvalid();
}
// 确认非法情况:数组只能跟随数组下标
// Confirm illegal condition: Array can only follow array subscript
if (
beforeTreeNode?.variable?.type &&
ViewVariableType.isArrayType(beforeTreeNode.variable.type) &&
@@ -270,12 +270,12 @@ export namespace ExpressionEditorTreeHelper {
) {
return itemInvalid();
}
// 忽略
// ignore
if (segment.type !== ExpressionEditorSegmentType.ObjectKey) {
return itemValid();
}
const treeNode = treeLayer.find(node => node.label === segment.objectKey);
// 确认非法情况:每一个 object key 必须对应一个 variable node
// Verify illegal condition: each object key must correspond to a variable node
if (!treeNode) {
return itemInvalid();
}

View File

@@ -20,7 +20,7 @@ import { useParams } from 'react-router-dom';
export const useOpenWorkflowDetail = () => {
const { bot_id: botId } = useParams<DynamicParams>();
/** 打开流程详情页 */
/** Open the process details page */
const openWorkflowDetailPage = ({
workflowId,
spaceId,

View File

@@ -103,7 +103,7 @@ interface WorkflowListReturn {
const defaultPageSize = 20;
/**
* 流程列表
* process list
*/
export function useWorkflowList({
pageSize = defaultPageSize,
@@ -112,7 +112,7 @@ export function useWorkflowList({
fetchWorkflowListApi = workflowApi.GetWorkFlowList.bind(workflowApi),
}: {
pageSize?: number;
/** 是否开启数据获取 */
/** Whether to enable data acquisition */
enabled?: boolean;
from?: WorkflowModalFrom;
fetchWorkflowListApi?: (
@@ -208,7 +208,7 @@ export function useWorkflowList({
if (params.bind_biz_type === BindBizType.Scene && params.bind_biz_id) {
const resp = await workflowApi.WorkflowListByBindBiz(params);
result.total = (resp.data.total as number) ?? 0;
// 设置流程权限
// Set process permissions
result.workflow_list = (resp.data.workflow_list ?? []).map(
(item): WorkflowInfo => {
const authInfo = {
@@ -223,7 +223,7 @@ export function useWorkflowList({
},
);
} else {
// 多人协作场景DEV 模式需要展示 Blockwise workflow除了流程列表引用
// Multiplayer collaboration scenarios, DEV mode needs to demonstrate Blockwise workflow (except for process list references)
Object.assign(params, {
schema_type_list: [SchemaType.FDL],
checker:
@@ -233,15 +233,15 @@ export function useWorkflowList({
});
const isDouyinBot = params.bind_biz_type === BindBizType.DouYinBot;
// 如果不是抖音分身模式,搜索参数不携带 bind_biz_id 参数
// 否则会导致某个工作流关联到 Agent 后0之后在该工作流添加子工作流时看不到工作流列表
// If not Douyin doppelganger mode, search parameters do not carry bind_biz_id parameters
// Otherwise, it will cause a workflow to be associated with the agent after 0, and then the workflow list will not be visible when a child workflow is added to the workflow
const fetchParams = isDouyinBot
? params
: omit(params, ['bind_biz_id']);
const resp = await fetchWorkflowListApi(fetchParams);
result.total = (resp.data.total as number) ?? 0;
// 设置流程权限
// Set process permissions
result.workflow_list = (resp.data.workflow_list ?? []).map(
(item): WorkflowInfo => {
let authInfo = {
@@ -339,13 +339,13 @@ export function useWorkflowList({
return pageData.pages[pageData.pages.length - 1].total ?? 0;
}, [pageData]);
// 复制
// copy
const handleCopy = async (item: WorkflowInfo) => {
if (!item.workflow_id || !spaceId) {
throw new CustomError('normal_error', 'miss workflowId or spaceID');
}
// 检查复制权限
// Check copy permissions
if (!item.authInfo.can_copy) {
throw new CustomError('normal_error', 'no copy permission');
}
@@ -388,10 +388,10 @@ export function useWorkflowList({
},
});
// 兜底服务主从延迟
// Bottom line leader/follower delay
await wait(300);
// 刷新列表
// refresh list
refetch();
} catch (error) {
reporter.error({
@@ -402,13 +402,13 @@ export function useWorkflowList({
}
};
// 删除
// delete
const handleDelete = async (item: WorkflowInfo) => {
if (!item.workflow_id || !spaceId) {
throw new CustomError('normal_error', 'miss workflowId or spaceID');
}
// 先检查删除权限
// Check the delete permission first
if (!item.authInfo.can_delete) {
throw new CustomError('normal_error', 'no delete permission');
}
@@ -422,7 +422,7 @@ export function useWorkflowList({
let deleteType = DeleteType.CanDelete;
// 从服务端查询删除模式
// Delete mode from server level query
const resp = await workflowApi.GetDeleteStrategy({
space_id: spaceId,
workflow_id: item.workflow_id,
@@ -461,10 +461,10 @@ export function useWorkflowList({
message: 'workflow_list_delete_row_success',
});
// 兜底服务主从延迟
// Bottom line leader/follower delay
await wait(300);
// 刷新列表
// refresh list
refetch();
} catch (error) {
reporter.error({
@@ -478,17 +478,17 @@ export function useWorkflowList({
}
};
return {
/** 是否可删除 */
/** Can it be deleted */
canDelete,
/** 删除策略 */
/** delete policy */
deleteType,
/** 删除方法 */
/** Delete method */
handleDelete: canDelete ? deleteFuc : undefined,
};
};
return {
// 列表筛选状态
// list filter status
flowType,
setFlowType,
flowMode,
@@ -505,31 +505,31 @@ export function useWorkflowList({
setOrderBy,
loginUserCreate,
setLoginUserCreate,
/** 更新筛选参数 */
/** Update filter parameters */
updatePageParam,
// 列表获取
/** 流程列表数据 */
// list acquisition
/** process list data */
workflowList,
/** 流程总数 */
/** total number of processes */
total,
/** 获取列表请求错误 */
/** Get list request error */
queryError,
/** 拉取下一页数据 */
/** Pull the next page of data */
fetchNextPage,
/** 是否有下一页 */
/** Is there a next page? */
hasNextPage,
/** 获取数据中 */
/** Acquiring data */
isFetching,
/** 获取下一页数据中 */
/** Get the next page of data */
isFetchingNextPage,
/** 加载状态 */
/** loading status */
loadingStatus,
/** 重新加载 */
/** Reload */
refetch,
// 列表操作
/** 复制流程 */
// list operation
/** replication process */
handleCopy,
// /** 删除流程 */
// /** Delete process */process */
handleDelete,
} as const;
}

View File

@@ -26,23 +26,23 @@ import { type ResourceInfo } from '@coze-arch/bot-api/plugin_develop';
export { type ResourceInfo };
export interface WorkflowResourceActionProps {
/* 刷新列表函数 */
/* refresh list function */
refreshPage?: () => void;
spaceId?: string;
/* 当前登录用户 id */
/* Current login user id */
userId?: string;
getCommonActions?: (
libraryResource: ResourceInfo,
) => NonNullable<TableActionProps['actionList']>;
}
export interface WorkflowResourceActionReturn {
/* 打开 workflow 创建弹窗 */
/* Open the workflow creation pop-up window */
openCreateModal: (flowMode?: WorkflowMode) => void;
/* 创建、删除等操作的全局弹窗,直接挂载到列表父容器上 */
/* Global pop-ups for create, delete, etc. are directly mounted on the list parent container */
workflowResourceModals: ReactNode[];
/* 在 Table 组件的 columns 的 render 里调用,返回 Table.TableAction 组件 */
/* Called in the render of the columns of the Table component, returning the Table. TableAction component */
renderWorkflowResourceActions: (record: ResourceInfo) => ReactNode;
/* 资源 item 点击 */
/* Resource item click */
handleWorkflowResourceClick: (record: ResourceInfo) => void;
}

View File

@@ -28,7 +28,7 @@ import { type CommonActionProps, type CommonActionReturn } from './type';
export const useCopyAction = (props: CommonActionProps): CommonActionReturn => {
const { spaceId } = props;
const navigate = useNavigate();
// 复制
// copy
const handleCopy = async (item: ResourceInfo) => {
if (!item.res_id || !spaceId) {
throw new CustomError('normal_error', 'miss workflowId or spaceID');
@@ -73,9 +73,9 @@ export const useCopyAction = (props: CommonActionProps): CommonActionReturn => {
},
});
// 兜底服务主从延迟
// Bottom line leader/follower delay
await wait(300);
// 复制后跳转到详情页
// After copying, jump to the details page
navigate(
`/work_flow?workflow_id=${data.workflow_id}&space_id=${spaceId}`,
);

View File

@@ -47,13 +47,13 @@ export const useCreateWorkflowModal = ({
nameValidators,
}: WorkflowResourceActionProps & {
from?: WorkflowModalFrom;
/** 当前项目 id只在项目内的 workflow 有该字段 */
/** The current project id, only the workflow within the project has this field */
projectId?: string;
bindBizType?: BindBizType;
bindBizId?: string;
onCreateSuccess?: ({ workflowId }: { workflowId: string }) => void;
goWorkflowDetail?: (workflowId?: string, spaceId?: string) => void;
/** 隐藏通过模板创建入口 */
/** Hide entry created through template */
hiddenTemplateEntry?: boolean;
nameValidators?: RuleItem[];
}) => {
@@ -89,7 +89,7 @@ export const useCreateWorkflowModal = ({
};
const workflowModalInitState = useMemo(() => {
// 即将支持,敬请期待
// Support soon, so stay tuned.
if (isWorkflowMode || FLAGS['bot.community.store_imageflow']) {
return {
productCategory: 'all',
@@ -154,14 +154,14 @@ export const useCreateWorkflowModal = ({
onCreateSuccess({ workflowId });
return;
}
// 编辑模式,不跳转,刷新当前列表
// Edit mode, do not jump, refresh the current list
if (formMode === 'update') {
refreshPage?.();
return;
}
const navigateDelay = 500;
// 由于后端数据同步慢,这里delay 500 ms
// Due to slow back-end data synchronization, there is a delay of 500 ms
setTimeout(() => {
goWorkflowDetail?.(workflowId, spaceId);
}, navigateDelay);

View File

@@ -39,7 +39,7 @@ export const useDeleteAction = (
const [deleteModalConfig, setDeleteModalConfig] =
useState<DeleteModalConfig>();
/**
* 逻辑来自 useWorkflowList@coze-workflow/components,由于入参变了,不再复用
* Logic comes from useWorkflowList (@code-workflow/components), no longer reused because imported parameters have changed
* @param item
*/
const handleDeleteWorkflowResource = async (item: ResourceInfo) => {
@@ -56,7 +56,7 @@ export const useDeleteAction = (
let deleteType = DeleteType.CanDelete;
// 从服务端查询删除模式
// Delete mode from server level query
const resp = await workflowApi.GetDeleteStrategy({
space_id: spaceId,
workflow_id: item.res_id,
@@ -95,10 +95,10 @@ export const useDeleteAction = (
message: 'workflow_list_delete_row_success',
});
// 兜底服务主从延迟
// Bottom line leader/follower delay
await wait(300);
// 刷新列表
// refresh list
refreshPage?.();
} catch (error) {
reporter.error({

View File

@@ -40,8 +40,8 @@ export const usePublishAction = ({
});
/**
* NOTICE: 此函数由商店侧维护, 可联系 @gaoding
* 发布/更新流程商品
* NOTICE: This function is maintained by the store side, you can contact @gaoding.
* Release/Update Process Product
*/
const onPublishStore = (item: ResourceInfo) => {
setFlowMode(
@@ -49,7 +49,7 @@ export const usePublishAction = ({
? WorkflowMode.Imageflow
: WorkflowMode.Workflow,
);
// 商店渲染流程需要 spaceId 信息, 在这个场景需要手动设置对应信息
// The store rendering process requires spaceId information, and in this scene, the corresponding information needs to be set manually
publishWorkflowModalHook.setSpace(spaceId);
publishWorkflowModalHook.showModal({
type: PublishWorkflowModal.WORKFLOW_INFO,

View File

@@ -32,7 +32,7 @@ export const useWorkflowResourceClick = (spaceId?: string) => {
goWorkflowDetail(workflowId, spaceId);
};
/** 打开流程编辑页 */
/** Open the process edit page */
const goWorkflowDetail = (workflowId?: string, sId?: string) => {
if (!workflowId || !sId) {
return;

View File

@@ -125,8 +125,8 @@ export const useWorkflowResourceMenuActions = (
...(getCommonActions?.(record) ?? []),
{
hide:
!enablePublishEntry || // 上架入口加白
(!FLAGS['bot.community.store_imageflow'] && isImageFlow) || // Imageflow 不支持商店
!enablePublishEntry || // The entrance on the shelf is white.
(!FLAGS['bot.community.store_imageflow'] && isImageFlow) || // Imageflow does not support stores
!isSelfCreator ||
bizExtend?.plugin_id === '0',
actionKey: 'publishWorkflowProduct',

View File

@@ -45,7 +45,7 @@ export const parseWorkflowResourceBizExtend = (
};
};
/**
* 转换 ResourceInfo 为编辑 workflow 所需的 WorkflowInfoLocal 结构
* Convert ResourceInfo to WorkflowInfoLocal structure required to edit workflow
* @param resource
*/
export const transformResourceToWorkflowEditInfo = (

View File

@@ -22,49 +22,49 @@ import { CustomError } from '@coze-arch/bot-error';
import { FileBizType } from '@coze-arch/bot-api/developer_api';
import { DeveloperApi } from '@coze-arch/bot-api';
/** 图片上传错误码 */
/** image upload error code */
export enum ImgUploadErrNo {
Success = 0,
/** 缺少文件 */
/** Missing document */
NoFile,
/** 上传失败 */
/** Upload failed. */
UploadFail,
/** 上传超时 */
/** Upload timed out */
UploadTimeout,
/** 获取 URL 失败 */
/** Failed to get URL */
GetUrlFail,
/** 校验异常, 但是不明确具体异常 */
/** Check exception, but not specific exception */
ValidateError,
/** 文件尺寸超出限制 */
/** File size exceeds limit */
MaxSizeError,
/** 文件类型不支持 */
/** File type not supported */
SuffixError,
/** 最大宽度限制 */
/** Maximum width limit */
MaxWidthError,
/** 最大高度限制 */
/** Maximum height limit */
MaxHeightError,
/** 最小宽度限制 */
/** minimum width limit */
MinWidthError,
/** 最小高度限制 */
/** Minimum height limit */
MinHeightError,
/** 固定宽高比 */
/** Fixed Aspect Ratio */
AspectRatioError,
}
export interface ImageRule {
/** 文件大小限制, 单位 b, 1M = 1 * 1024 * 1024 */
/** File size limit, unit b, 1M = 1 * 1024 * 1024 */
maxSize?: number;
/** 文件后缀 */
/** file suffix */
suffix?: string[];
/** 最大宽度限制 */
/** Maximum width limit */
maxWidth?: number;
/** 最大高度限制 */
/** Maximum height limit */
maxHeight?: number;
/** 最小宽度限制 */
/** minimum width limit */
minWidth?: number;
/** 最小高度限制 */
/** Minimum height limit */
minHeight?: number;
/** 固定宽高比 */
/** Fixed Aspect Ratio */
aspectRatio?: number;
}
@@ -82,34 +82,34 @@ type UploadResult =
};
/**
* Workflow 图片上传
* Workflow image upload
*/
class ImageUploader {
/** 任务 ID, 用于避免 ABA 问题 */
/** Task ID to avoid ABA issues */
private taskId = 0;
/**
* 上传模式
* - api 直接使用接口上传
* - uploader 上传到视频云服务, 走 workflow 服务. !海外版未经过测试
* upload mode
* - API directly uses the interface to upload
* - uploader to Video Cloud as a Service, go workflow service.! Overseas version has not been tested
*/
mode: 'uploader' | 'api' = 'uploader';
/** 校验规则 */
/** validation rule */
rules?: ImageRule;
/** 上传的文件 */
/** Uploaded file */
file?: File;
/** 展示 Url, 添加文件后生成, 用于预览 */
/** Show URLs, generated after adding files, for preview */
displayUrl?: string;
/** 上传状态 */
/** Upload Status */
isUploading = false;
/** 超时时间 */
/** timeout */
timeout?: number;
/** 校验结果 */
/** verification result */
validateResult?: {
isSuccess: boolean;
errNo: ImgUploadErrNo;
msg?: string;
};
/** 上传结果 */
/** Upload result */
uploadResult?: UploadResult;
constructor(config?: {
@@ -122,7 +122,7 @@ class ImageUploader {
this.timeout = config?.timeout ?? this.timeout;
}
/** 选择待上传文件 */
/** Select the file to upload */
async select(file: File) {
if (!file) {
throw new CustomError('normal_error', '选择文件为空');
@@ -140,16 +140,16 @@ class ImageUploader {
});
}
/** 上传图片 */
/** Upload image */
async upload() {
// 未选择文件或文件不符合要求
// No file was selected or the file does not meet the requirements
if (!this.file || !this.validateResult?.isSuccess || this.isUploading) {
return;
}
this.isUploading = true;
// 添加任务 ID,避免 ABA 问题
// Add task IDs to avoid ABA issues
this.taskId += 1;
const currentId = this.taskId;
@@ -208,7 +208,7 @@ class ImageUploader {
if (!uri) {
return;
}
// 获取 url
// Get URL
const resp = await workflowApi
.SignImageURL(
{
@@ -301,7 +301,7 @@ class ImageUploader {
reset() {
this.file = undefined;
if (this.displayUrl) {
// 是内部链接
// Is an internal link
URL.revokeObjectURL(this.displayUrl);
this.displayUrl = undefined;
}
@@ -319,7 +319,7 @@ class ImageUploader {
const rules = this.rules || {};
// 文件尺寸
// file size
if (rules.maxSize) {
if (this.file.size > rules.maxSize) {
this.validateResult = {
@@ -333,7 +333,7 @@ class ImageUploader {
}
}
// 文件后缀
// file suffix
if (Array.isArray(rules.suffix) && rules.suffix.length > 0) {
const fileExtension = getFileExtension(this.file.name);
if (!rules.suffix.includes(fileExtension)) {
@@ -348,7 +348,7 @@ class ImageUploader {
}
}
// 图片尺寸
// image size
const { width, height } = await getImageSize(this.displayUrl);
if (!width || !height) {
@@ -445,14 +445,14 @@ function getBase64(file: Blob): Promise<string> {
});
}
/** 获取文件名后缀 */
/** Get filename suffix */
function getFileExtension(name: string) {
const index = name.lastIndexOf('.');
return name.slice(index + 1).toLowerCase();
}
/**
* @param url 获取图片宽高
* @Param url Get image width and height
*/
function getImageSize(url: string): Promise<{ width: number; height: number }> {
return new Promise((resolve, reject) => {
@@ -468,9 +468,9 @@ function getImageSize(url: string): Promise<{ width: number; height: number }> {
}
/**
* 格式化文件大小
* @param bytes 文件大小
* @param decimals 小数位数, 默认 2 位
* Format file size
* @param bytes file size
* @Param decimals, default 2 digits
* @example
* formatBytes(1024); // 1KB
* formatBytes('1024'); // 1KB

View File

@@ -52,7 +52,7 @@ interface ImageUploaderProps {
style?: CSSProperties;
readonly?: boolean;
disabled?: boolean;
/** 图片上传限制 */
/** image upload restrictions */
rules?: ImageRule;
value?: { url: string; uri: string } | undefined;
validateStatus?: SelectProps['validateStatus'];
@@ -61,13 +61,13 @@ interface ImageUploaderProps {
}
interface ImagePopoverWrapperProps {
/** 图片地址 */
/** Image address */
url?: string;
maxWidth?: number;
maxHeight?: number;
minWidth?: number;
minHeight?: number;
/** 是否支持预览 */
/** Whether to support preview */
enablePreview?: boolean;
children?: React.ReactElement;
}
@@ -215,7 +215,7 @@ const ImageUploader: FC<ImageUploaderProps> = ({
return 'image/*';
}, [rules?.suffix]);
/** 整体区域支持交互 */
/** Overall Area Support Interaction */
const wrapCanAction = useMemo(
() => !uri && !loading && !isError && !disabled && !readonly,
[uri, loading, isError, disabled, readonly],

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @coze-arch/max-line-per-function */
import { useCallback, useEffect, useRef, useState } from 'react';
@@ -24,36 +24,36 @@ import { Toast } from '@coze-arch/coze-design';
import ImageUploader, { ImgUploadErrNo } from './image-uploader';
interface UseImageUploaderParams {
/** 图片限制条件 */
/** image restrictions */
rules?: ImageUploader['rules'];
/** 上传模式 */
/** upload mode */
mode?: ImageUploader['mode'];
/** 上传配置 */
/** upload configuration */
timeout?: ImageUploader['timeout'];
}
interface UseImageUploaderReturn {
/** 图片标识, 用于提交给服务 */
/** Image ID for submission to the service */
uri: string;
/** 图片展示地址 */
/** Picture display address */
url: string;
/** 文件名 */
/** file name */
fileName: string;
/** 上传中状态 */
/** Uploading status */
loading: boolean;
/** 上传失败状态 */
/** Upload failed status */
isError: boolean;
/** 上传图片 */
/** Upload image */
uploadImg: (file: File) => Promise<ImageUploader['uploadResult']>;
/** 清除已上传图片 */
/** Clear uploaded images */
clearImg: () => void;
/** 上传失败后重试 */
/** Retry after upload failure */
retryUploadImg: () => Promise<ImageUploader['uploadResult']>;
/**
* 设置初始状态, 用于回显服务下发的数据
* Set the initial state for echoing data sent by the service
*
* @param val 对应值
* @param isMerge 是否 merge 模式, merge 模式仅更新传入字段. 默认 false
* @param val corresponding value
* @Param isMerge Whether to merge mode, merge mode only updates incoming fields. Default false
*/
setImgValue: (
val: { uri?: string; url?: string; fileName?: string },
@@ -61,7 +61,7 @@ interface UseImageUploaderReturn {
) => void;
}
/** 缓存文件名 */
/** cache filename */
const fileNameCache: Record<string, string> = Object.create(null);
// eslint-disable-next-line max-lines-per-function
@@ -102,14 +102,14 @@ export default function useImageUploader(
setFileName(targetFileName);
}
// Merge 模式, 未设置的值清空
// Non-Merge mode, unset values are cleared
if (!isMerge) {
setUrl(targetDisplayUrl ?? '');
setUri(targetUri ?? '');
setFileName(targetFileName ?? '');
}
// 文件名特殊逻辑, 根据 uri 从缓存重映射文件名
// Filename special logic, remapping filenames from cache based on URIs
if (!targetFileName) {
if (targetUri && fileNameCache[targetUri]) {
setFileName(fileNameCache[targetUri]);
@@ -130,12 +130,12 @@ export default function useImageUploader(
const uploadImg = useCallback(
async (file: File): Promise<ImageUploader['uploadResult'] | undefined> => {
await uploaderRef.current.select(file);
// 图片校验不通过
// The picture verification failed.
if (!uploaderRef.current.validateResult?.isSuccess) {
Toast.error(
uploaderRef.current.validateResult?.msg || '图片不符合要求',
);
// @ts-expect-error 此处 validateResult.isSuccess false
// @ts-expect-error here validateResult.isSuccess is false
return uploaderRef.current.validateResult;
}
@@ -146,10 +146,10 @@ export default function useImageUploader(
await uploaderRef.current.upload();
setLoading(false);
// 上传结果
// Upload result
const { uploadResult } = uploaderRef.current;
// 无上传结果说明上传取消
// No upload result indicates that the upload is cancelled.
if (!uploadResult) {
return;
}
@@ -159,7 +159,7 @@ export default function useImageUploader(
if (uploadResult.isSuccess) {
Toast.success(I18n.t('file_upload_success'));
setUri(uploadResult.uri);
// FIXME: 合理的设计应该用 uri 进行缓存, 但是 Imageflow 初期只存储了 url, 使用 url 作为临时方案
// FIXME: A reasonable design should cache with URIs, but Imageflow initially only stored URLs, using URLs as a temporary solution
fileNameCache[uploadResult.url] = `${file.name}`;
} else {
Toast.error(uploadResult.msg);
@@ -172,7 +172,7 @@ export default function useImageUploader(
const retryUploadImg = useCallback(async (): Promise<
ImageUploader['uploadResult']
> => {
// 重传前置检查, 有文件且校验通过
// Resend pre-check, there is a file and the verification is passed
if (
!uploaderRef.current?.file ||
!uploaderRef.current?.validateResult?.isSuccess
@@ -189,7 +189,7 @@ export default function useImageUploader(
await uploaderRef.current.upload();
setLoading(false);
// 上传结果
// Upload result
const uploadResult = uploaderRef.current.uploadResult || {
isSuccess: false,
errNo: ImgUploadErrNo.UploadFail,

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
/** Text 组件,超出自动 ... 并且展示 tooltip */
/** Text component, beyond automatic... and show tooltip */
import { type FC } from 'react';
import { Typography } from '@coze-arch/coze-design';

View File

@@ -26,35 +26,35 @@ interface Auth {
export type WorkflowInfo = Workflow & Auth;
/**
* 打开弹窗场景, 主要用于 log
* Open the pop-up scene, mainly for logging
*
* WorkflowAddNode 场景有特殊处理
* WorkflowAddNode scenes have special handling
*/
export enum WorkflowModalFrom {
/** 流程详情添加子流程 */
/** Process details Add subprocess */
WorkflowAddNode = 'workflow_addNode',
/** bot skills 打开 */
/** Open in bot skills */
BotSkills = 'bot_skills',
/** 在抖音分身场景的 ide 打开 */
/** Open ide in Douyin doppelganger scene */
BotSkillsDouyin = 'bot_skills_douyin_ide',
/** 在 bot 多 agent skills 打开 */
/** Open in bot multi-agent skills */
BotMultiSkills = 'bot_multi_skills',
/** bot triggers 打开 */
/** Open in bot triggers */
BotTrigger = 'bot_trigger',
/** bot 快捷方式打开 */
/** Bot shortcut open */
BotShortcut = 'bot_shortcut',
/** 空间下流程列表 */
/** process list under space */
SpaceWorkflowList = 'space_workflow_list',
/** 来自 workflow as agent */
/** From workflow as agent */
WorkflowAgent = 'workflow_agent',
/** 社会场景 workflow 列表 */
/** Social scene workflow list */
SocialSceneHost = 'social_scene_host',
/** 项目引入资源库文件 */
/** project import repository file */
ProjectImportLibrary = 'project_import_library',
/** 项目内 workflow 画布添加子流程 */
/** Add subflow to workflow canvas in project */
ProjectWorkflowAddNode = 'project_workflow_addNode',
/**
* 项目内 workflow 资源列表添加 workflow 资源
* List of workflow resources in the project Add workflow resources
*/
ProjectAddWorkflowResource = 'project_add_workflow_resource',
}

View File

@@ -18,37 +18,37 @@ import cronstrue from 'cronstrue/i18n';
import { I18n } from '@coze-arch/i18n';
const langMap = {
// 简体中文
// Simplified Chinese
'zh-CN': 'zh_CN',
zh: 'zh-CN',
// 繁体中文
// Traditional Chinese
zh_TW: 'zh_TW',
// 英语
// English
'en-US': 'en',
en: 'en',
// 日语
// Japanese
'ja-JP': 'ja',
ja: 'ja',
// 韩语
// Korean
'ko-KR': 'ko',
ko: 'ko',
// 法语
// French
'fr-FR': 'fr',
fr: 'fr',
// 德语
// German
'de-DE': 'de',
de: 'de',
// 意大利语
// Italian
'it-IT': 'it',
it: 'it',
// 西班牙语
// Spanish
'es-ES': 'es',
es: 'es',
};
// 校验使用 cronjob 翻译结果
// Verify translation results using cronjob
export const isCronJobVerify = cronJob => {
// 仅支持 6 位 cronjob后端限制
// Only 6-bit cronjobs are supported (backend limit).
const parts = cronJob?.split(' ');
if (parts?.length !== 6) {
return false;
@@ -59,9 +59,9 @@ export const isCronJobVerify = cronJob => {
throwExceptionOnParseError: true,
});
// 额外校验一下字符串是否包含 null undefined
// 1 2 3 31 1- 1 在上午 03:02:01, 限每月 31 号, 或者为星期一, 一月至undefined
// 1 2 3 31 1 1#6 在上午 03:02:01, 限每月 31 号, 限每月的null 星期一, 仅于一月份
// Extra check if the string contains null undefined
// 1 2 3 31 1- 1 at 03:02:01 am, limited to the 31st of each month, or for Monday, January to undefined
// 1 2 3 31 1 1 #6 at 03:02:01 am, limited to the 31st of each month, limited to null Mondays of each month, only in January
if (rs.includes('null') || rs.includes('undefined')) {
return false;
}

View File

@@ -18,7 +18,7 @@ import dayjs from 'dayjs';
let getIsIPadCache: boolean | undefined;
/**
* gpt-4 提供的代码
* Code provided by gpt-4
*/
export const getIsIPad = () => {
if (typeof getIsIPadCache === 'undefined') {
@@ -34,7 +34,7 @@ export const getIsIPad = () => {
return getIsIPadCache;
};
/* 时间戳转文本,并省略年份或日期*/
/* Timestamp converts text and omits the year or date*/
export const formatOmittedDateTime = time => {
if (!time) {
return '';
@@ -46,20 +46,20 @@ export const formatOmittedDateTime = time => {
let formatStr: string;
if (!today.isSame(day, 'year')) {
// 不是当年,展示年份
// Not the year, show the year
formatStr = 'YYYY-MM-DD HH:mm';
} else if (!today.isSame(day, 'day')) {
// 不是当天, 展示日期
// Not the day, the display date.
formatStr = 'MM-DD HH:mm';
} else {
// 当天只展示时间
// Show time only on the day
formatStr = 'HH:mm';
}
return day.format(formatStr);
};
/** 等待 */
/** wait */
export const wait = (ms: number) =>
new Promise(r => {
setTimeout(r, ms);
@@ -68,7 +68,7 @@ export const wait = (ms: number) =>
import { reporter as infraReporter } from '@coze-arch/logger';
/**
* 流程使用的 slardar 上报实例
* The slardar reporting instance used by the process
*/
export const reporter = infraReporter.createReporterWithPreset({
namespace: 'workflow',

View File

@@ -37,7 +37,7 @@ import { type WorkflowCommitListProps } from './type';
export interface CommitItemProps {
className?: string;
data: VersionMetaInfo;
/** 是否选中 */
/** Is it selected? */
isActive?: boolean;
readonly?: WorkflowCommitListProps['readonly'];
enablePublishPPE?: WorkflowCommitListProps['enablePublishPPE'];
@@ -45,9 +45,9 @@ export interface CommitItemProps {
onPublishPPE?: WorkflowCommitListProps['onPublishPPE'];
onResetToCommit?: WorkflowCommitListProps['onResetToCommit'];
onShowCommit?: WorkflowCommitListProps['onShowCommit'];
/** 隐藏操作下拉菜单 */
/** Hide action drop-down menu */
hiddenActionMenu?: boolean;
/** 隐藏 commitId */
/** Hide commitId */
hideCommitId?: boolean;
}

View File

@@ -76,11 +76,11 @@ const WorkflowCommitListComp: FC<WorkflowCommitListProps> = withQueryClient(
enabled: true,
});
/** scrollcontainer */
/** Scroll container */
const scrollContainerRef = useRef<HTMLDivElement | null>(null);
/** 监听触底的observer */
/** Monitor the bottom observer */
const intersectionObserverDom = useRef<HTMLDivElement>(null);
// 是否触底
// Is it bottoming out?
const [inViewPort] = useInViewport(intersectionObserverDom, {
root: () => scrollContainerRef.current,
threshold: 0.8,
@@ -90,9 +90,9 @@ const WorkflowCommitListComp: FC<WorkflowCommitListProps> = withQueryClient(
updatePageParam({ type });
}, [type, updatePageParam]);
// 首次effect不执行这个是切换状态的effect
// The first effect is not executed, this is the effect of switching the state
useUpdateEffect(() => {
// 当筛选项改变时,回到顶部
// When the filter item changes, return to the top
if (scrollContainerRef.current) {
scrollContainerRef.current.scrollTo({
top: 0,
@@ -100,7 +100,7 @@ const WorkflowCommitListComp: FC<WorkflowCommitListProps> = withQueryClient(
}
}, [queryParams]);
// 获取下一页逻辑
// Get next page logic
useEffect(() => {
if (!inViewPort) {
return;
@@ -142,12 +142,12 @@ const WorkflowCommitListComp: FC<WorkflowCommitListProps> = withQueryClient(
}
const timelineType = (data: VersionMetaInfo, index) => {
// PPE 历史, 在线激活
// PPE history, online activation
if (type === OperateType.PubPPEOperate) {
return !data.offline ? 'ongoing' : 'default';
}
// 提交历史和发布历史, 最新的激活
// Submission history and release history, latest activations
return index === 0 ? 'ongoing' : 'default';
};

View File

@@ -19,33 +19,33 @@ import {
type OperateType,
} from '@coze-workflow/base/api';
/** 流程提交历史列表组件 */
/** Process Submission History List Component */
export interface WorkflowCommitListProps {
className?: string;
spaceId: string;
workflowId: string;
/** 操作类型 */
/** operation type */
type: OperateType;
/** 只读模式, 只读历史卡片不可点, 不影响 action */
/** Read-only mode, read-only history cards cannot be clicked, does not affect action */
readonly?: boolean;
/** 每页拉取数量, 默认 10 */
/** Number of pulls per page, default 10 */
limit?: number;
/** 当前选中项 */
/** current selection */
value?: string;
/** 是否展示当前节点 */
/** Whether to display the current node */
showCurrent?: boolean;
/** 是否支持发布到 PPE 功能 */
/** Whether to support publishing to PPE function */
enablePublishPPE?: boolean;
/** 隐藏 commitId (commitId可读性较差,非专业用户不需要感知) */
/** Hide the commitId (the commitId is less readable, and non-professional users do not need to perceive it) */
hideCommitId?: boolean;
/** 卡片点击 */
/** Card click */
onItemClick?: (item: VersionMetaInfo) => void;
/** 恢复到某版本点击 */
/** Restore to a certain version Click */
onResetToCommit?: (item: VersionMetaInfo) => void;
/** 查看某版本点击 */
/** To view a version click */
onShowCommit?: (item: VersionMetaInfo) => void;
/** 发布到多环境点击 */
/** Publish to Multi-environment Click */
onPublishPPE?: (item: VersionMetaInfo) => void;
/** 点击[现在] */
/** Click [Now] */
onCurrentClick?: (currentKey: string) => void;
}

View File

@@ -36,9 +36,9 @@ interface VersionHistoryParams {
spaceId: string;
workflowId: string;
type: OperateType;
/** 每页请求数量, 默认 10 */
/** Number of requests per page, default 10 */
pageSize?: number;
/** 是否启动请求, 默认 false */
/** Whether to start the request, default false */
enabled?: boolean;
}

View File

@@ -52,7 +52,7 @@ import { CustomError } from '@coze-arch/bot-error';
import { FileBizType, IconType } from '@coze-arch/bot-api/developer_api';
import s from './index.module.less';
/** 输入合规校验异常的错误码 */
/** Enter error code for compliance exception */
const sensitiveWordsErrorCode = ['702095075', '702095081'];
const { Checkbox } = Form;
@@ -62,13 +62,13 @@ export interface RuleItem {
}
interface EditWorkFlowPropsInner {
/** 流程类型 */
/** process type */
flowMode?: WorkflowMode;
mode: 'update' | 'add';
visible: boolean;
// 默认confirmdisabled
// Default confirmed disabled
/** 自定义弹窗标题 */
/** Custom pop-up title */
customTitleRender?: (title: React.ReactNode) => React.ReactNode;
initConfirmDisabled?: boolean;
@@ -78,17 +78,17 @@ interface EditWorkFlowPropsInner {
flowMode?: EditWorkFlowPropsInner['flowMode'];
}) => void;
onCancel?: () => void;
/** @deprecated 未使用 */
/** @deprecated unused */
spaceID?: string;
getLatestWorkflowJson?: () => Promise<WorkflowJSON>;
bindBizId?: string;
bindBizType?: BindBizType;
/** 当前项目 id只在项目内的 workflow 有该字段 */
/** The current project id, only the workflow within the project has this field */
projectId?: string;
nameValidators?: RuleItem[];
}
/** 表单值 */
/** form value */
interface FormValue {
icon_uri: UploadValue;
name: string;
@@ -97,7 +97,7 @@ interface FormValue {
create_conversation?: boolean;
}
/** 获取弹窗标题 */
/** Get pop-up title */
function getModalTitle(
mode: EditWorkFlowPropsInner['mode'],
flowMode: EditWorkFlowPropsInner['flowMode'],
@@ -204,7 +204,7 @@ export function CreateWorkflowModal({
name: values?.name,
desc: values?.target ? values.target : '',
space_id: workFlow.space_id || '',
// 更新头像等信息不需要重新test run
// There is no need to re-test run to update the avatar and other information.
ignore_status_transfer: true,
schema_type: values?.schema_type || workFlow?.schema_type,
};
@@ -437,7 +437,7 @@ export function CreateWorkflowModal({
data-testid="workflow.list.create.name.input"
/>
{/* 只有项目内创建 Chatflow 时才可以绑定会话 */}
{/* Sessions can only be bound when Chatflow is created within the project */}
{mode === 'add' && projectId && flowMode === WorkflowMode.ChatFlow ? (
<Checkbox
fieldClassName={s['conversation-field']}

View File

@@ -19,7 +19,7 @@ import { I18n } from '@coze-arch/i18n';
import { WORKFLOW_LIST_STATUS_ALL } from '@/workflow-modal/type';
/** 流程所有者选项, 全部/我的 */
/** Process Owner Options, All/Mine */
export const scopeOptions = [
{
label: I18n.t('workflow_list_scope_all'),
@@ -31,7 +31,7 @@ export const scopeOptions = [
},
];
/** 流程状态选项, 全部/已发布/未发布 */
/** Process Status Options, All/Published/Unpublished */
export const statusOptions = [
{
label: I18n.t('workflow_list_status_all'),
@@ -47,7 +47,7 @@ export const statusOptions = [
},
];
/** 流程排序选项, 创建时间/更新时间 */
/** Process sorting options, creation time/update time */
export const sortOptions = [
{
label: I18n.t('workflow_list_sort_create_time'),

View File

@@ -98,7 +98,7 @@ export const WorkflowBotButton: FC<WorkflowBotButtonProps> = ({
}
}
// 如果已添加,展示已添加按钮
// If added, display the Added button
if (isAdded) {
return (
<Popconfirm
@@ -117,8 +117,8 @@ export const WorkflowBotButton: FC<WorkflowBotButtonProps> = ({
</Popconfirm>
);
}
// 未添加,判断发布状态
// 未发布,展示下面的按钮
// Not added, judge the release status
// Unpublished, show the button below
if (!canAdd) {
let key: I18nKeysNoOptionsType = 'workflow_add_not_allow_before_publish';
if (isFromWorkflow) {
@@ -143,7 +143,7 @@ export const WorkflowBotButton: FC<WorkflowBotButtonProps> = ({
</Tooltip>
);
}
// 已发布并且未添加,展示添加按钮
// Published and not added, show the add button
if (!isAdded) {
return (
<LoadingButton

View File

@@ -36,7 +36,7 @@ export const DeleteButton = ({
};
const handleDelete = () =>
// 使用 promise 让按钮出现 loading 的效果,参见
// Use promises to make the button appear loading, see
// https://semi.design/zh-CN/feedback/popconfirm
new Promise((resolve, reject) => {
onDelete?.()
@@ -45,7 +45,7 @@ export const DeleteButton = ({
resolve(true);
})
.catch(error => {
// 处理错误
// Handle errors
logger.error({
error: error as Error,
eventName: 'delete workflow error',

View File

@@ -120,7 +120,7 @@ export const WorkflowCard: FC<WorkflowCardProps> = props => {
: undefined;
const renderStatusValue = () => {
// 添加项目里的工作流节点、官方示例不展示发布状态
// Add workflow nodes in the project, official examples do not show release status
if (
isSelectProjectCategory(context?.modalState) ||
workflowCategory === WorkflowCategory.Example
@@ -138,7 +138,7 @@ export const WorkflowCard: FC<WorkflowCardProps> = props => {
return null;
};
const renderBottomLeftDesc = () => {
// 商品底部
// bottom of the product
if (!isTypeWorkflow(data)) {
const timeRender = `${I18n.t('workflow_add_list_updated')} ${formatTime(
data.meta_info.listed_at,
@@ -175,7 +175,7 @@ export const WorkflowCard: FC<WorkflowCardProps> = props => {
);
}
// 用户创建的,展示修改时间
// User-created, showing modification time
if (isSpaceWorkflow || workflowCategory === WorkflowCategory.Example) {
const showCreator =
(creator !== MineActiveEnum.Mine && isTeam) ||
@@ -214,7 +214,7 @@ export const WorkflowCard: FC<WorkflowCardProps> = props => {
);
}
// 官方模板,展示创作者
// Official template, showcasing creators
if (!isSpaceWorkflow) {
return (
<div className={styles.creator}>

View File

@@ -148,7 +148,7 @@ export const WorkflowParameters: FC<WorkflowParametersProps> = ({
return context?.modalState.dataSourceType === DataSourceType.Workflow;
}
const getParameters = (): Array<WorkflowParameterItem> => {
// 这么拆分虽然有点冗余, 但是可以正确进行类型推导
// Although this split is a bit redundant, it allows for correct type derivation
if (isTypeWorkflow(data)) {
return (
data.start_node?.node_param?.input_parameters?.map(item => {

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