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

@@ -59,8 +59,8 @@ export const ProjectIDEClient: React.FC<
return (
<IDEClient
options={options}
// 兼容 mnt e2e 环境,在 e2e 环境下,高度会被坍缩成 0
// 因此需要额外的样式兼容
// Compatible with mnt e2e environment, in e2e environment, the height will collapse to 0
// Therefore, additional style compatibility is required
// className={(window as any)._mnt_e2e_testing_ ? 'e2e-flow-container' : ''}
className="e2e-flow-container"
>

View File

@@ -38,7 +38,7 @@ const MAX_DEEP = 5;
const RESOURCE_FOLDER_CONTEXT_KEY = 'resourceFolderContextKey';
/**
* 乐观 ui 创建文件的 ID 默认前缀
* Optimistic UI creates the default prefix for the ID of the file
*/
const OPTIMISM_ID_PREFIX = 'resource-folder-optimism-id-';
@@ -48,7 +48,7 @@ const MORE_TOOLS_CLASS_NAME = 'more-tools-class-name';
enum ItemStatus {
Normal = 'normal',
Disabled = 'disabled', // 禁止操作
Disabled = 'disabled', // Prohibited operation
}
const COLOR_CONFIG = {

View File

@@ -103,9 +103,9 @@ const useCreateEditResource = ({
const [createResourceInfo, setCreateResourceInfo] = useState<{
/**
* 用于定位渲染位置的 index
* 资源在文件夹后面,所有资源前面
* 文件夹在当前文件夹下最前面
* The index used to locate the render position
* Resources are behind folders and in front of all resources
* The folder is at the front of the current folder
*/
index: number;
parentId: IdType;
@@ -196,7 +196,7 @@ const useCreateEditResource = ({
}
/**
* 同名检测
* same name test
*/
// if (editResourceRef.current) {
// const parentFolder = createResourceInfoRef.current
@@ -237,7 +237,7 @@ const useCreateEditResource = ({
if (createResourceInfoRef?.current) {
/**
* 新建资源
* new resource
*/
const parentPath =
resourceMap.current[createResourceInfoRef?.current?.parentId]?.path;
@@ -251,7 +251,7 @@ const useCreateEditResource = ({
});
} else if (editResourceRef?.current) {
/**
* 编辑资源
* editing resources
*/
const path = editResourceRef?.current?.path || [];
const parentPath = path.slice(0, path?.length - 1);
@@ -287,7 +287,7 @@ const useCreateEditResource = ({
}
if (selectResource.type !== 'folder' && selectResource?.path) {
// 如果不是 folder 则选中父亲 folder
// If it is not a folder, select the parent folder.
selectResource =
resourceMap.current[
selectResource.path[selectResource.path.length - 2]

View File

@@ -57,7 +57,7 @@ const useFocusResource = ({
itemHeight: config?.itemHeight || ITEM_HEIGHT,
});
// 如果在视图内, 则不滚
// If in view, do not roll
if (
scrollTop > scrollWrapper.current.scrollTop &&
scrollTop <

View File

@@ -70,7 +70,7 @@ const useMouseEvent = ({
iconRender,
config,
}: {
draggable: boolean; // 是否可以拖拽
draggable: boolean; // Can you drag and drop?
uniqId: string;
updateId: () => void;
resourceTreeWrapperRef: React.MutableRefObject<HTMLDivElement | null>;
@@ -100,7 +100,7 @@ const useMouseEvent = ({
const isFocusRef = useRef(false);
const dragService = useIDEService<DragService>(DragService);
/**
* 存储拖拽过程中是否合法的字段
* Store legal fields during drag and drop
*/
const [draggingError, setDraggingError] = useState<string>('');
@@ -114,7 +114,7 @@ const useMouseEvent = ({
});
/**
* 用于开启拖拽到 mainPanel 下打开资源的方法
* The method used to open resources by dragging and dropping to mainPanel
*/
const dragAndOpenResource = e => {
if (!config?.resourceUriHandler) {
@@ -140,7 +140,7 @@ const useMouseEvent = ({
},
backdropTransform: {
/**
* 通过边缘检测的方式,阻止 lm-cursor-backdrop 元素进入树组件内
* Prevent lm-cursor-backdrop elements from entering the tree component through edge detection
*/
clientX: (eventX: number) =>
Math.max(
@@ -180,11 +180,11 @@ const useMouseEvent = ({
);
/**
* 用于记录拖拽过程中,当前 hover 元素的 父元素 id。
* Used to record the parent ID of the current hover element during drag and drop.
*/
const hoverItemParentId = useRef<string | null>(null);
/**
* 用于记录拖拽过程中,当前 hover 元素的 父元素及其下钻所有节点的id表
* The ID table used to record the parent element of the current hover element and all nodes drilled down during the drag and drop process
*/
const [highlightItemMap, setHighlightItemMap] = useState<ResourceMapType>({});
@@ -238,7 +238,7 @@ const useMouseEvent = ({
);
/**
* 将文件夹下的文件给过滤掉,防止拖拽之后失去层级结构
* Filter out the files in the folder to prevent the hierarchy from being lost after dragging
*/
resourceList = resourceList.filter(resource => {
const { type, path } = resourceMap.current[resource.id];
@@ -273,7 +273,7 @@ const useMouseEvent = ({
};
}
// 这里要选中一次是保证拖动前选中正确的 item
// This should be selected once to ensure that the correct item is selected before dragging.
if (typeof target === 'object' && !e.shiftKey && !e.metaKey) {
let currentSelected: ResourceType | null = null;
@@ -306,7 +306,7 @@ const useMouseEvent = ({
return;
}
// 点到右键的 panel 中 啥也别操作
// Click on the right-click panel and don't operate anything.
if (target === CLICK_CONTEXT_MENU) {
return;
}
@@ -329,7 +329,7 @@ const useMouseEvent = ({
}
/**
* 如果点击了 more tools 三颗点,那需要先将临时选中表重置为只选中该 item。 用于右键菜单的消费。
* If you click the more tools three dots, you need to reset the temporary selection table to select only this item first. For right-click menu consumption.
*/
if (target.customTag === MORE_TOOLS_CLASS_NAME) {
const nextSelected = { [currentSelected.id]: currentSelected };
@@ -337,7 +337,7 @@ const useMouseEvent = ({
return;
}
// 如果右键, 并且点击在已经被选中的资源上,则不再做操作,因为要弹操作栏
// If you right-click and click on a resource that has already been selected, you will no longer do the operation because the action bar will pop up.
if (
e.ctrlKey ||
(e.button === 2 &&
@@ -358,7 +358,7 @@ const useMouseEvent = ({
return;
}
// 批量一下子多选
// multiple selection in batches
let nextSelected: any = getResourceListFromIdToId({
resourceTree: resourceTreeRef.current,
from: firstSelectedId,
@@ -379,7 +379,7 @@ const useMouseEvent = ({
}
changeTempSelectedMap(nextSelected);
} else if (e.metaKey) {
// 挑着多选
// Choose more
let nextSelected = { ...tempSelectedMapRef.current };
if (currentSelected?.id) {
if (nextSelected[currentSelected.id]) {
@@ -452,7 +452,7 @@ const useMouseEvent = ({
}
/**
* 开始拖拽
* Start dragging
*/
if (!isDraggingRef.current) {
startDrag(e);
@@ -469,7 +469,7 @@ const useMouseEvent = ({
uniqId,
});
// 点到右键的 panel 中 啥也别操作
// Click on the right-click panel and don't operate anything.
if (target === CLICK_CONTEXT_MENU) {
return;
}
@@ -516,7 +516,7 @@ const useMouseEvent = ({
uniqId,
});
// 点到右键的 panel 中 啥也别操作
// Click on the right-click panel and don't operate anything.
if (target === CLICK_CONTEXT_MENU || !target) {
return;
}

View File

@@ -47,10 +47,10 @@ const isPointInRect = (
point.y > rect.y &&
point.y < rect.y + rect.height;
const BORDER_GAP = 8; // 默认边缘阈值
const BORDER_GAP = 8; // Default edge threshold
/**
* 相对于 findTargetElement ,增加了边缘检测功能。
* 当鼠标在 资源目录边缘, 会算作聚焦在 root 节点
* Compared with findTargetElement, the edge detection function is added.
* When the mouse is on the edge of the resource directory, it will be regarded as focusing on the root node
*/
const getElementByXY = ({
e,
@@ -96,7 +96,7 @@ const findTargetElement = (
elm: HTMLElement | null,
uniqueId: string,
/**
* 遇到该 className 会进行记录,并且返回 id 的时候会带上该 className
* The className will be recorded when encountered, and the className will be included when the id is returned.
*/
customClassName?: string,
): TargetType | string => {
@@ -176,7 +176,7 @@ const validateDrag = (
const { name, path, id } = target;
/**
* 只要有一个文件是自己层级挪动到自己层级的则 return
* As long as there is a file that is moved to its own level, it will be returned.
*/
const selfList = resourceList.filter(resource => {
const resourcePath = resource.path || [];
@@ -189,7 +189,7 @@ const validateDrag = (
.join(', ')} into ${name}`;
}
// 校验是不是爹移到儿子
// Check if the father moved to the son.
const notAllowedList = resourceList.filter(resource =>
(path || []).includes(String(resource.id)),
);
@@ -200,7 +200,7 @@ const validateDrag = (
.join(', ')} into ${name}`;
}
// 校验移动之后层级是不是过深
// Check if the level is too deep after the move
const maxDeep = resourceList.reduce(
(max, resource) => Math.max(max, (resource?.maxDeep || 0) + 1),
0,

View File

@@ -77,7 +77,7 @@ const useRegisterCommand = ({
});
}
} else if (command.execute) {
// 如果有自定义的 execute 函数才会需要重新注册
// If there is a custom execute function, it will only need to be re-registered.
if (commandRegistry.getCommand(command.id)) {
commandRegistry.unregisterCommand(command.id);
}

View File

@@ -18,8 +18,8 @@ import { type RightPanelConfigType } from '../../type';
import { ContextMenuConfigMap } from './constant';
/**
* 主要替换资源树默认支持的 右键菜单配置,
* 并且对三方注入的右键菜单的 id 进行包装
* The main replacement resource tree is supported by default, right-click menu configuration,
* And wraps the id of the right-click menu injected by the three parties.
*/
export const handleConfig = (
baseConfig: RightPanelConfigType[],

View File

@@ -37,7 +37,7 @@ const useSelectedChange = ({
updateContext({ currentSelectedId: selected });
// 将聚焦的 path 上的文件夹都展开
// Expand folders on the focused path
const path = resourceMap.current[selected]?.path || [];
path.forEach(pathKey => {
delete collapsedMapRef.current[pathKey];

View File

@@ -69,35 +69,35 @@ import s from './index.module.less';
interface RefType {
/**
* 创建文件夹
* Create Folder
*/
createFolder: () => void;
/**
* 创建资源
* Create a resource
*/
createResource: (type: string) => void;
/**
* 重命名资源
* rename resource
*/
renameResource: (id: IdType) => void;
/**
* 手动关闭右键菜单
* Close the right-click menu manually
*/
closeContextMenu: () => void;
/**
* 收起所有文件夹
* Close all folders
*/
collapseAll: () => void;
/**
* 展开所有文件夹
* Expand all folders
*/
expandAll: () => void;
/**
* 手动聚焦
* manual focus
*/
focus: () => void;
/**
* 手动失焦
* Manual out of focus
*/
blur: () => void;
}
@@ -110,16 +110,16 @@ interface Props {
disabled?: boolean;
/**
* 主要的资源类型,非必填。
* 主要用于快捷键创建资源的默认类型。
* Main resource type, optional.
* Mainly used to shortcut the default type for creating resources.
*/
defaultResourceType?: string;
/**
* 是否使用乐观 ui;
* false 时,onChange 失效;
* Whether to use optimistic UI;
* When false, onChange is invalid;
* default = true
*
* 传入 loadingRender 时,会对乐观保存的 item 尾部增加一个渲染块,由外部控制渲染
* When passing in loadingRender, a render block will be added to the tail of the optimistically saved item, which will be rendered by external control
*/
useOptimismUI?:
| boolean
@@ -128,22 +128,22 @@ interface Props {
};
/**
* 当前选中的资源 id 受控的
* The currently selected resource id, controlled
*/
selected?: string;
/**
* 是否渲染每个 item 末尾的 more 按钮hover 等同于 右键
* Whether to render the more button at the end of each item, hover is equivalent to right click
*/
renderMoreSuffix?: RenderMoreSuffixType;
/**
* 用于 name 校验的配置
* Configuration for name validation
*/
validateConfig?: ValidatorConfigType;
/**
* 支持搜索, 高亮展示
* Support search, highlight
*/
searchConfig?: {
searchKey?: string;
@@ -151,84 +151,84 @@ interface Props {
};
/**
* 可选。
* 传入则是受控的收起展开树。
* 不传则内部自己维护树
* Optional.
* The incoming is a controlled retract expansion tree.
* If you don't pass it on, you will maintain the tree internally.
*/
collapsedMap?: Record<string, boolean>;
setCollapsedMap?: (v: Record<string, boolean>) => void;
/**
* 树变更的回调函数,依赖 useOptimismUI true
* Callback function for tree changes, depending on useOptimismUI to be true.
*/
onChange?: (resource: ResourceType[]) => void;
/**
* 单击选中资源的回调,仅支持非 folder 类型资源
* Click the callback for the selected resource, only non-folder type resources are supported
*/
onSelected?: (id: string | number, resource: ResourceType) => void;
/**
* 拖拽完成之后的回调
* Callback after dragging is complete
*/
onDrag?: (v: DragPropType) => void;
/**
* 修改 name 之后的回调
* Callback after modifying name
*/
onChangeName?: (v: ChangeNameType) => void;
/**
* 创建资源的回调
* Create a callback for a resource
*/
onCreate?: (v: CreateResourcePropType) => void;
/**
* 删除的回调。该方法不会被乐观 ui 逻辑改写,最好业务层加一个二次确认逻辑,删除之后走数据更新的方式来更新树组件
* Deleted callback. This method will not be overwritten by optimistic ui logic. It is best to add a secondary confirmation logic to the business layer. After deletion, take the data update method to update the tree component.
*/
onDelete?: (ids: ResourceType[]) => void;
/**
* 用于自定义配置渲染资源的 icon
* @returns react 节点
* Icons for customizing configuration rendering resources
* @returns react node
*/
iconRender?: (v: CommonRenderProps) => React.ReactElement | undefined;
/**
* 用于自定义配置每一项末尾的元素
* Use to customize the elements at the end of each item
*/
suffixRender?: {
width: number; // 用于文本超长的 tooltip 偏移量计算的,是一个必填字段
width: number; // Used for the calculation of the tooltip offset for very long text, is a required field
render: (v: CommonRenderProps) => React.ReactElement | undefined;
};
/**
* 用于自定义配置每一个资源的文本渲染器。
* 如果采用自定义渲染,则需要自己实现搜索高亮能力
* @returns react 节点
* Text renderer for custom configuration of each resource.
* If you use custom rendering, you need to implement the search highlighting capability yourself
* @returns react node
*/
textRender?: (v: CommonRenderProps) => React.ReactElement | undefined;
/**
* 右键菜单配置
* @param v 当前临时选中的资源列表。 可以通过 id 判断是否是根文件。(ROOT_KEY: 根文件 id
* @returns 组件内置注册好的命令和菜单,详见 BaseResourceContextMenuBtnType 枚举
* right-click menu configuration
* @Param v List of currently temporarily selected resources. You can determine whether it is a root file by id. (ROOT_KEY: root file id)
* @Returns component built-in registered commands and menus, see BaseResourceContextMenuBtnType enumeration
*/
contextMenuHandler?: (v: ResourceType[]) => RightPanelConfigType[];
/**
* 右键菜单弹窗展示和隐藏的回调。
* Right-click menu pop-ups to show and hide callbacks.
*/
onContextMenuVisibleChange?: (v: boolean) => void;
/**
* 禁用右键菜单,主要是兼容在 popover 的场景内
* Disable the right-click menu, mainly compatible in the popover scene
*/
contextMenuDisabled?: boolean;
/**
* 一些用于杂七杂八的配置项
* Some configuration items for miscellaneous purposes
*/
config?: ConfigType;
/**
* 能力黑名单
* ability blacklist
*/
powerBlackMap?: {
dragAndDrop?: boolean;
@@ -236,7 +236,7 @@ interface Props {
};
/**
* 列表为空的渲染组件
* Rendering component with empty list
*/
empty?: React.ReactElement;
}
@@ -288,7 +288,7 @@ const ResourceFolder = forwardRef<RefType, Props>(
const renderMoreSuffix = contextMenuDisabled ? false : _renderMoreSuffix;
/**
* 临时选中的表
* Temporarily selected table
*/
const [tempSelectedMapRef, setTempSelectedMap] = useStateRef<
Record<string, ResourceType>
@@ -297,12 +297,12 @@ const ResourceFolder = forwardRef<RefType, Props>(
});
/**
* 打平的树
* Flattened Tree
*/
const resourceMap = useRef<ResourceMapType>(_resourceMap || {});
const changeResourceMap = nextMap => {
resourceMap.current = nextMap;
// 变更之后维护临时选中表
// Maintain the temporary selection table after the change
tempSelectedMapRef.current = Object.keys(
tempSelectedMapRef.current,
).reduce((pre, cur) => {
@@ -321,7 +321,7 @@ const ResourceFolder = forwardRef<RefType, Props>(
}, [_resourceMap]);
/**
* 处理一系列收起展开的 hook
* Handle a series of retracted and unfolded hooks
*/
const { collapsedMapRef, handleCollapse, setCollapsed, collapsedState } =
useCollapsedMap({
@@ -331,7 +331,7 @@ const ResourceFolder = forwardRef<RefType, Props>(
});
/**
* 用于渲染的树
* Tree for rendering
*/
const [resourceTreeRef, setResourceTree] = useStateRef<ResourceType>(
{
@@ -376,7 +376,7 @@ const ResourceFolder = forwardRef<RefType, Props>(
}, [disabled]);
/**
* 用于收敛树组件的滚动,聚焦逻辑的 hook
* Hooks for scrolling, focusing logic of convergent tree components
*/
const { scrollInView, scrollWrapper, tempDisableScroll } = useFocusResource(
{
@@ -418,7 +418,7 @@ const ResourceFolder = forwardRef<RefType, Props>(
}, [_resourceTree]);
/**
* 处理选中的资源变更之后的副作用
* Handling side effects after changes to selected resources
*/
const selectedIdRef = useSelectedChange({
selected,
@@ -606,7 +606,7 @@ const ResourceFolder = forwardRef<RefType, Props>(
const list = renderResourceList || [];
/**
* 为空数组,或者数组中只有一个 root 节点
* Is an empty array, or there is only one root node in the array
*/
if (
(list.length === 0 ||
@@ -672,7 +672,7 @@ const ResourceFolder = forwardRef<RefType, Props>(
const isExpand = !collapsedMapRef.current[resource.id];
const highlightItem =
!powerBlackMap?.folder && // 不支持文件夹则不需支持拖拽时候的高亮
!powerBlackMap?.folder && // If folders are not supported, there is no need to support highlighting when dragging.
!!dragAndDropContext.highlightItemMap[resource.id];
const preHighlightItem =
resourceList.current[i - 1]?.id !== ROOT_KEY &&
@@ -686,7 +686,7 @@ const ResourceFolder = forwardRef<RefType, Props>(
const extraClassName = [
/**
* 拖拽过程中的样式
* Styles during drag and drop
*/
...(highlightItem
? [
@@ -700,7 +700,7 @@ const ResourceFolder = forwardRef<RefType, Props>(
: []),
isSelected ? 'item-is-selected' : '',
/**
* 拖拽过程中的 hover 态优先级 大于 临时选中态的优先级
* The priority of the hover state during the dragging process is greater than the priority of the temporarily selected state
*/
...(isTempSelected && !highlightItem
? [
@@ -740,7 +740,7 @@ const ResourceFolder = forwardRef<RefType, Props>(
);
})}
{emptyRender()}
{/* 添加 24px 底部间距,标识加载完全 */}
{/* Add 24px bottom spacing to identify fully loaded */}
<div style={{ padding: 12 }}></div>
</div>
</div>

View File

@@ -35,7 +35,7 @@ const MoreTools = ({
}) => {
const handleClick = e => {
/**
* 这里将 event 的 currentTarget 设置成树组件的 wrapper 元素,保证 contextMenu 的 matchItems 方法可以正常遍历。
* Here, the currentTarget of the event is set to the wrapper element of the tree component to ensure that the matchItems method of the contextMenu can be traversed normally.
*/
e.currentTarget = resourceTreeWrapperRef.current;
contextMenuCallback(e, [resource]);

View File

@@ -37,9 +37,9 @@ export enum ResourceTypeEnum {
export type ItemType = ResourceTypeEnum | string;
export interface ResourceStatusType {
// 是否是草稿态
// Is it a draft?
draft?: boolean;
// 错误内容 & 个数
// Error content & number
problem?: {
status?: 'normal' | 'error' | 'warning';
number?: number;
@@ -110,7 +110,7 @@ export interface CommonComponentProps {
isDragging: boolean;
isExpand?: boolean;
/**
* 是否处于乐观 ui 的保存阶段
* Is it in the preservation stage of optimistic UI?
*/
isOptimismSaving?: boolean;
useOptimismUI?:
@@ -172,7 +172,7 @@ export interface CommonRenderProps {
resource: ResourceType;
isSelected?: boolean;
isTempSelected?: boolean;
isExpand?: boolean /** 只有 resource.type === folder 的时候才会有该字段 */;
isExpand?: boolean /** This field is only available when resource.type === folder */;
}
export interface ContextType {
@@ -222,9 +222,9 @@ export interface CustomValidatorPropsType {
export interface ValidatorConfigType {
/**
* @param label 当前输入框的文本
* @param parentPath 从 root 开始到 父级的路径
* @param resourceTree 当前的资源树
* @param label the text of the current text box
* @Param parentPath, path from root to parent
* @Param resourceTree The current resource tree
* @returns Promise<string>
*/
/** */
@@ -233,8 +233,8 @@ export interface ValidatorConfigType {
) => string | Promise<string>;
/**
* 默认: absolute;
* absolute: 不占用树的文档流,错误文案覆盖上去;
* Default: absolute;
* Absolute: document flow that does not occupy the tree, and error copy is overwritten;
*/
errorMsgPosition?: 'absolute';
errorMsgStyle?: React.CSSProperties;
@@ -268,12 +268,12 @@ export interface CommandOption {
onClick?: () => void;
}
/** 内置的顶部工具栏的按钮组 */
/** Built-in button group for top toolbar */
export enum BuildInToolOperations {
/** 搜索过滤按钮 */
/** Search filter button */
CreateFile = 'CreateFile',
CreateFolder = 'CreateFolder',
/** 展开收起文件夹 */
/** Expand Collapse Folder */
ExpandFolder = 'ExpandFolder',
}
@@ -287,41 +287,41 @@ export type RightPanelConfigType =
tooltip?: string;
/**
* 这里有两类 命令。
* 1. 不在外部插件中提前注册好的,需要组件内部动态注册的。往往是该组件树定制化的菜单项。比如 校验并运行该资源
* 那这个 execute 字段是一个必填项
* 2. 在外部插件中提前注册好的,往往是需要配合快捷键一起使用的,并且具有一定的普适性的命令。比如创建资源,复制资源等。
* 那这个 execute 不需要配置,的回调在 plugin 中注册的地方触发
* 上下文可以通过 RESOURCE_FOLDER_CONTEXT_KEY 这个 key 从 ide 上下文中获取
* Here are two categories of orders.
* 1. If it is not registered in advance in the external plug-in, it needs to be dynamically registered inside the component. Often it is a menu item customized by the component tree. For example, check and run the resource
* Then this execute field is a required field
* 2. Registered in advance in external plugins, often need to be used with shortcuts, and have certain universal commands. Such as creating resources, copying resources, etc.
* Then this execute does not need to be configured, and the callback is triggered where it is registered in the plugin.
* The context can be obtained from the IDE context by RESOURCE_FOLDER_CONTEXT_KEY this key
*/
execute?: () => void;
}
| {
// 分割线
// dividing line
type: 'separator';
};
export interface ConfigType {
/**
* 每个资源的高度
* The height of each resource
*/
itemHeight?: number;
/**
* 半个 icon 的宽度,用于左侧折线的计算。
* The width of half an icon is used for the calculation of the left polyline.
*/
halfIconWidth?: number;
/**
* 每个文件夹下缩进的宽度
* The width of indentation under each folder
*/
tabSize?: number;
/**
* 文件夹下钻最大的深度
* Folder drill down to maximum depth
*/
maxDeep?: number;
/**
* 资源 name 输入框配置
* Resource name text box configuration
*/
input?: {
className?: string;
@@ -330,14 +330,14 @@ export interface ConfigType {
};
/**
* 拖拽过程中的预览框配置项
* Preview box configuration items during drag and drop
*/
dragUi?: {
disable?: boolean;
wrapperClassName?: string;
/**
* 特别说明: 可以通过配置这里的 top left 来设置相对鼠标的偏移量
* Special note: You can set the offset relative to the mouse by configuring top and left here
*/
wrapperStyle?: React.CSSProperties;
};
@@ -346,9 +346,9 @@ export interface ConfigType {
}
export interface ResourceFolderContextType {
id?: string; // folder 组件唯一的 id
currentSelectedId?: IdType; // 当前选中的资源 id
tempSelectedMap?: Record<string, ResourceType>; // 当前临时选中的资源 map
id?: string; // Folder component unique id
currentSelectedId?: IdType; // The currently selected resource ID.
tempSelectedMap?: Record<string, ResourceType>; // Currently temporarily selected resource map
onEnter?: () => void;
onDelete?: () => void;
onCreateFolder?: () => void;

View File

@@ -102,7 +102,7 @@ export const getResourceById = (
};
/**
* 用 shift 修饰键的时候, 从 from 的到 to 的中间全部选中,包括下钻文件。 dfs
* When modifying keys with shift, select all the middle from from to to, including the drill-down files. dfs
*/
export const getResourceListFromIdToId = ({
resourceTree,
@@ -200,7 +200,7 @@ export const sortResourceList = (
return folderList.concat(sourceList) as ResourceType[];
};
// 后续要优化算法的话 ,得在树打平的算法中,记录每个文件夹的高度,这样在 change 的时候不需要重复计算。 packages/api-builder/base/src/utils/resource-folder/index.ts mapResourceTree
// If you want to optimize the algorithm in the future, you have to record the height of each folder in the tree leveling algorithm, so that you don't need to repeatedly calculate when changing. packages/api-builder/base/src/utils/resource-folder/index.ts mapResourceTree
export const calcOffsetTopByCollapsedMap = (props: {
selectedId: string;
resourceTree: ResourceType;
@@ -209,7 +209,7 @@ export const calcOffsetTopByCollapsedMap = (props: {
}) => {
const { selectedId, resourceTree, collapsedMap, itemHeight } = props;
let num = -1; // 因为从 root 开始, root 不展示,所以从 -1 开始算
let num = -1; // Because starting from root, root does not display, it starts from -1
let finish = false;
const dfs = (resource: ResourceType) => {
@@ -267,12 +267,12 @@ export const getResourceTravelIds = (ctx: {
travelResource(resource, item => {
const info = resourceMap[item.id];
// 被删除的资源、文件夹不展示
// Deleted resources and folders are not displayed
if (!info || info.status === 'deprecated') {
return false;
}
// 折叠的文件夹折叠后,不遍历只节点
// Folding folders after folding, do not traverse only nodes
if (info.type === ResourceTypeEnum.Folder && collapsedMap[info.id]) {
ids.push(item.id);
return false;
@@ -381,10 +381,10 @@ export const mapResourceTree = (resourceTree): ResourceMapType => {
return { maxDeep: path.length - 1 };
}
// 文件夹要加一,因为能加文件
// You need to add one to the folder, because you can add files.
let maxDeep = path.length + (resource.type === 'folder' ? 1 : 0);
// 当前资源是否处于提交状态
// Is the current resource in a committed state?
let editDraft = resource.edit_status === 'draft';
if (resource.children) {
@@ -395,7 +395,7 @@ export const mapResourceTree = (resourceTree): ResourceMapType => {
]);
maxDeep = Math.max(maxDeep, deep);
// 文件夹 editDraft 跟随草稿走,只要内部有一个为草稿,本文件夹也为草稿
// Folder editDraft follows the draft, as long as there is one inside as a draft, this folder is also a draft
editDraft = !!(editDraft || status);
});
}
@@ -405,7 +405,7 @@ export const mapResourceTree = (resourceTree): ResourceMapType => {
path,
maxDeep: maxDeep - path.length,
/**
* 随业务放开
* release with business
*/
// draft: editDraft,
// problem: {
@@ -462,9 +462,9 @@ export const flatTree = (
};
/**
* 计算当前文件夹下,新建的资源所处的位置。
* 文件夹:在当前文件夹下顶部
* 资源:在当前文件夹下的文件夹末尾,所有资源顶部
* Calculates the location of the newly created resource in the current folder.
* Folder: under the current folder top
* Resources: At the end of the folder under the current folder, at the top of all resources
*/
export const getCreateResourceIndex = ({
resourceList,
@@ -510,9 +510,9 @@ export function baseValidateNames(props: { label: string; nameTitle: string }) {
const simple = true;
// 设定默认值,避免 propExtra 只传入一个配置
// Set the default value to avoid propExtra passing only one configuration
// 检测 name 是否空
// Check if name is empty
if (!label) {
return simple ? 'Empty Key' : `${nameTitle} name can not be empty`;
}
@@ -521,14 +521,14 @@ export function baseValidateNames(props: { label: string; nameTitle: string }) {
return simple ? 'Length exceeds' : `${nameTitle} name length exceeds limit`;
}
// 必须由字母开头
// Must begin with a letter
if (!/^[A-Za-z]/.test(label)) {
return simple
? 'Must start with letter'
: `${nameTitle} name must start with a letter`;
}
// 检测 name 的命名规则
// Detection of naming rules for names
if (!/^[A-Za-z][0-9a-zA-Z_]*$/.test(label)) {
return simple
? 'only ASCII letters, digits, and _'

View File

@@ -21,10 +21,10 @@ import { type WidgetService } from '../plugins/create-preset-plugin/widget-servi
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export interface WidgetContext<T = any> {
uri?: URI; // 当前 widget 的 uri
store: T; // 当前 widget store
uri?: URI; // The URI of the current widget
store: T; // The current widget store
widget: WidgetService;
services: ProjectIDEServices; // 全局的 ide 服务
services: ProjectIDEServices; // Global IDE service
}
export const WidgetContext = Symbol('WidgetContext');

View File

@@ -23,7 +23,7 @@ import { type ProjectIDEWidget } from '@/widgets/project-ide-widget';
import { type WidgetContext } from '@/context/widget-context';
/**
* 用于提供当前 focus 的 widget 上下文
* The widget context used to provide the current focus
*/
export const useActivateWidgetContext = (): WidgetContext => {
const currentWidget = useCurrentWidgetFromArea(LayoutPanelType.MAIN_PANEL);

View File

@@ -17,7 +17,7 @@
import { useIDEGlobalStore } from '../context';
export const useCommitVersion = () => {
// 内置了 shallow 操作,无需 useShallow
// Built-in shallow operation, no useShallow
// eslint-disable-next-line @coze-arch/zustand/prefer-shallow
const { version, patch } = useIDEGlobalStore(store => ({
version: store.version,

View File

@@ -46,7 +46,7 @@ const getTabArea = (shell: ApplicationShell, uri?: URI): Area | undefined => {
}
});
// 右边分屏不展示 hover icon
// The split screen on the right does not show the hover icon.
if (children?.length === 1) {
return undefined;
} else if (currentTabIndex === 1) {
@@ -57,10 +57,10 @@ const getTabArea = (shell: ApplicationShell, uri?: URI): Area | undefined => {
};
/**
* 获取当前 uri 的资源在哪个分屏下
* left: 左边分屏
* right: 右边分屏
* undefined: 未分屏
* Get the resource of the current URI under which split screen
* Left: Left split screen
* Right: right split screen
* Undefined: not split screen
*/
export const useSplitScreenArea = (
uri?: URI,
@@ -73,8 +73,8 @@ export const useSplitScreenArea = (
useEffect(() => {
setArea(getTabArea(shell, uri));
const listener = () => {
// 本次 uri 是否在当前 tab不是不执行
// 分屏过程中会出现中间态,布局变更时盲目执行会导致时序异常问题
// Is this URI in the current tab, not not executed
// There will be an intermediate state during the split-screen process, and blind execution when the layout is changed will lead to abnormal timing problems
const uriInCurrentTab = tabBar?.titles.some(title =>
compareURI((title.owner as ReactWidget)?.uri, uri),
);

View File

@@ -21,8 +21,8 @@ import { type ProjectIDEWidget } from '@/widgets/project-ide-widget';
import { type WidgetContext } from '../context/widget-context';
/**
* 获取当前的 WidgetContext
* registry renderContent 内调用
* Get the current WidgetContext
* Called within the registry's renderContent
*/
export function useCurrentWidgetContext<T>(): WidgetContext<T> {
const currentWidget = useCurrentWidget() as ProjectIDEWidget;

View File

@@ -45,7 +45,7 @@ const useCurrentWidgetActivate = (cb: ActivateCallback) => {
};
/**
* 获取当前 widget 的 location
* Get the location of the current widget
*/
export const useIDELocation = () => {
const currentWidget = useCurrentWidget() as ProjectIDEWidget;
@@ -70,7 +70,7 @@ export const useIDELocation = () => {
};
/**
* 获取当前 widget 的 query 参数
* Get the query parameters of the current widget
*/
export const useIDEParams = () => {
const currentWidget = useCurrentWidget() as ProjectIDEWidget;

View File

@@ -41,10 +41,10 @@ export const useIDENavigate = () => {
const uri = new URI(`${URI_SCHEME}://${value}`);
const isUIBuilder = uri.displayName === UI_BUILDER_URI.displayName;
if (value && value !== '/' && !isUIBuilder) {
// 调用 openService
// Call openService
view.open(uri);
} else {
// 如果没有要打开的 widget就只打开主面板
// If there is no widget to open, just open the main panel
view.openPanel(isUIBuilder ? 'ui-builder' : 'dev');
}
navigate(addPreservedSearchParams(url), options);

View File

@@ -18,8 +18,8 @@ import { type interfaces } from 'inversify';
import { useIDEContainer } from '@coze-project-ide/client';
/**
* 获取 IDE 的 IOC 模块
* 和 flow-ide/client 包内容相同,但可以支持在业务侧如 workflow 内调用
* Get the IOC module of the IDE
* The same content as the flow-ide/client package, but it can be called on the business side such as workflow
* @param identifier
*/
export function useIDEServiceInBiz<T>(

View File

@@ -28,14 +28,14 @@ export const useMessageEventService = () =>
useIDEService<MessageEventService>(MessageEventService);
/**
* 获取向 widget 发送信息函数的 hooks
* Get hooks for the function that sends information to the widget
*/
export const useSendMessageEvent = () => {
const messageEventService = useMessageEventService();
const navigate = useIDENavigate();
/**
* 向以 uri 为索引的 widget 发送信息
* Send a message to a widget indexed by URI
*/
const send = useCallback(
<T>(target: string | URI, data: MessageEvent<T>) => {
@@ -49,8 +49,8 @@ export const useSendMessageEvent = () => {
);
/**
* 向以 uri 为索引的 widget 发送信息,并且打开/激活此 widget
* 此函数比较常用
* Send a message to a widget indexed with URIs, and open/activate the widget
* This function is more commonly used
*/
const sendOpen = useCallback(
<T>(target: string | URI, data: MessageEvent<T>) => {
@@ -68,26 +68,26 @@ export const useSendMessageEvent = () => {
};
/**
* 监听向指定 uri 对应的唯一 widget 发送消息的 hook
* 监听消息的 widget 一定是知道 this.uri所以入参无须支持 string
* 注:虽然 widget.uri 的值是会变得,但其 withoutQuery().toString() 一定是不变的,所以 uri 可以认定为不变
* Listens for hooks that send messages to the unique widget corresponding to the specified URI
* The widget listening to the message must know this.uri, so imported parameters do not need to support string.
* Note: Although the value of widget.uri will change, its withoutQuery ().toString () must be unchanged, so uri can be considered unchanged
*/
export const useListenMessageEvent = (
uri: URI,
cb: (e: MessageEvent) => void,
) => {
const messageEventService = useMessageEventService();
// 尽管 uri 对应的唯一 key 不会变化,但 uri 内存地址仍然会变化,这里显式的固化 uri 的不变性
// Although the unique key corresponding to the URI does not change, the URI memory address will still change, and the invariance of the cured URI is explicit here
const uriRef = useRef(uri);
// 保证 callback 函数的可变性
// Guarantee the variability of the callback function
const listener = useMemoizedFn(() => {
const queue = messageEventService.on(uri);
queue.forEach(cb);
});
useEffect(() => {
// 组件挂在时去队列中取一次,有可能在组件未挂载前已经被发送了消息
// When the component is hung, go to the queue to pick it up once. It is possible that the message has been sent before the component is not mounted.
listener();
const disposable = messageEventService.onSend(e => {

View File

@@ -19,7 +19,7 @@ import { type ViewService } from '@/plugins/create-preset-plugin/view-service';
import { useProjectIDEServices } from './use-project-ide-services';
/**
* 获取 ProjectIDE 所有视图操作
* Get all view operations of Project IDE
*/
export const useViewService = (): ViewService => {
const projectIDEServices = useProjectIDEServices();

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
/** 透传 sdk */
/** pass-through sdk */
export {
IDEClient,
ReactWidget,

View File

@@ -30,12 +30,12 @@ import {
import { ViewService } from '../create-preset-plugin/view-service';
const CUSTOM_COMMAND = {
// 在左侧分屏打开
// Open split screen on left
SPLIT_LEFT: {
id: 'view.custom.split-left',
label: I18n.t('project_ide_tabs_open_on_left'),
},
// 在右侧分屏打开
// Open in split screen on the right
SPLIT_RIGHT: {
id: 'view.custom.split-right',
label: I18n.t('project_ide_tabs_open_on_right'),
@@ -49,10 +49,10 @@ const CUSTOM_COMMAND = {
function getAllTabsCount(dockPanel: FlowDockPanel): number {
let count = 0;
// 遍历 DockPanel 中的所有小部件
// Traverse all widgets in DockPanel
Array.from(dockPanel.children()).forEach(widget => {
if (widget instanceof TabBar) {
// 累计 TabBar 中的所有标签页数
// Accumulate all tab pages in TabBar
count += widget.titles.length;
}
});
@@ -70,9 +70,9 @@ export const createContextMenuPlugin: PluginCreator<void> = definePluginCreator(
const shortcutsService =
ctx.container.get<ShortcutsService>(ShortcutsService);
/**
* 更改标题
* Change the title
*/
// 更新 command 标题 label
// Update command title label
command.updateCommand(Command.Default.VIEW_CLOSE_CURRENT_WIDGET, {
label: I18n.t('project_ide_tabs_close'),
});
@@ -95,7 +95,7 @@ export const createContextMenuPlugin: PluginCreator<void> = definePluginCreator(
execute: widget => {
viewService.splitScreen('left', widget);
},
// 分屏功能在所有 tab 大于 1 时才可以使用
// Split screen function can only be used when all tabs are greater than 1
isEnabled: () => {
const tabCounts = getAllTabsCount(shell.mainPanel);
return tabCounts > 1;
@@ -105,46 +105,46 @@ export const createContextMenuPlugin: PluginCreator<void> = definePluginCreator(
execute: widget => {
viewService.splitScreen('right', widget);
},
// 分屏功能在所有 tab 大于 1 时才可以使用
// Split screen function can only be used when all tabs are greater than 1
isEnabled: () => {
const tabCounts = getAllTabsCount(shell.mainPanel);
return tabCounts > 1;
},
});
/**
* 注册 menu
* Registration menu
*/
// 关闭
// close
menuService.addMenuItem({
command: Command.Default.VIEW_CLOSE_CURRENT_WIDGET,
selector: '.lm-TabBar-tab',
});
// 关闭其他
// Close other
menuService.addMenuItem({
command: Command.Default.VIEW_CLOSE_OTHER_WIDGET,
selector: '.lm-TabBar-tab',
});
// 关闭所有
// Close all
menuService.addMenuItem({
command: Command.Default.VIEW_CLOSE_ALL_WIDGET,
selector: '.lm-TabBar-tab',
});
// 刷新标签
// refresh label
menuService.addMenuItem({
command: CUSTOM_COMMAND.REFRESH.id,
selector: '.lm-TabBar-tab',
});
// 分割线
// dividing line
menuService.addMenuItem({
type: 'separator',
selector: '.lm-TabBar-tab',
});
// 向左分屏
// Split screen to the left
menuService.addMenuItem({
command: CUSTOM_COMMAND.SPLIT_LEFT.id,
selector: '.lm-TabBar-tab',
});
// 向右分屏
// Split screen to the right
menuService.addMenuItem({
command: CUSTOM_COMMAND.SPLIT_RIGHT.id,
selector: '.lm-TabBar-tab',

View File

@@ -139,7 +139,7 @@ export class PresetContribution
this.services.contextmenu.registerContextMenu(menus, registry.match);
}
});
// 覆写全屏逻辑
// Override full screen logic
this.commandRegistry.unregisterCommand(Command.Default.VIEW_FULL_SCREEN);
this.commandRegistry.registerCommand(
{
@@ -176,7 +176,7 @@ export class PresetContribution
}
private createLayout(shell: ApplicationShell) {
// 设置 panel 存储到 widgetManager
// Set up panel storage to widgetManager
const uiBuilderPanel = new BoxPanel();
uiBuilderPanel.id = UI_BUILDER_URI.displayName;
this.widgetManager.setWidget(UI_BUILDER_URI.toString(), uiBuilderPanel);
@@ -238,7 +238,7 @@ export class PresetContribution
main: {
splitOptions: {
maxSplitCount: 2,
splitOrientation: 'horizontal', // 只支持水平分屏
splitOrientation: 'horizontal', // Only horizontal split screen is supported.
},
dockPanelOptions: {
spacing: 6,

View File

@@ -31,11 +31,11 @@ import {
import { ViewService } from './view-service';
/**
* 获取 service 操作
* 全局任意位置均可调用
* command命令系统注册
* contextmenu:右键菜单注册
* view视图操作
* Acquire service operation
* It can be called anywhere in the world
* Command: Command system registration
* Contextmenu: right-click menu registration
* View: view operation
*/
@injectable()
export class ProjectIDEServices {

View File

@@ -24,7 +24,7 @@ import {
} from '@coze-project-ide/client';
import { Tooltip } from '@coze-arch/coze-design';
// 自定义 IDE HoverService 样式
// Custom IDE HoverService style
@injectable()
class TooltipContribution implements LabelHandler {
@inject(HoverService) hoverService: HoverService;
@@ -41,21 +41,21 @@ class TooltipContribution implements LabelHandler {
}
renderer(uri: URI, opt?: any): React.ReactNode {
// 下边的 opacitywidth 设置原因:
// semi 源码位置:https://github.com/DouyinFE/semi-design/blob/main/packages/semi-foundation/tooltip/foundation.ts#L342
// semi trigger 元素判断,本次自定义 semi 组件没有 focus 内部元素。
// The reason for the opacity and width settings below:
// Semi source code location: https://github.com/DouyinFE/semi-design/blob/main/packages/semi-foundation/tooltip/foundation.ts#L342
// Semi has a trigger element to judge, and this custom semi component does not have a focus internal element.
return opt?.content ? (
<Tooltip
key={opt.content}
content={opt.content}
position={opt.position}
// 覆盖设置重置 foundation opacity,避免 tooltip 跳动
// Override settings to reset foundation opacity to avoid tooltip jumping
style={{ opacity: 1 }}
trigger="custom"
getPopupContainer={() => document.body}
visible={true}
>
{/* 宽度 0 避免被全局样式影响导致 Tooltip 定位错误 */}
{/* Width 0 to avoid Tooltip positioning errors due to global style influence */}
<div style={{ width: 0 }}></div>
</Tooltip>
) : null;

View File

@@ -70,7 +70,7 @@ export class ViewService {
this.onFullScreenModeChangeEmitter.event;
/**
* 主侧边栏功能集合
* Main Sidebar Feature Collection
*/
public primarySidebar = {
onSidebarVisibleChange: this.onSidebarVisibleChange,
@@ -90,7 +90,7 @@ export class ViewService {
getVisible: () => this.shell.secondarySidebar.isVisible,
changeVisible: (vis: boolean) => {
if (vis) {
// 打开前需要判断面板是否已经注册打开
// Before opening, you need to determine whether the panel has been registered and opened.
const secondaryPanel = this.widgetManager.getWidgetFromURI(
SECONDARY_SIDEBAR_URI,
);
@@ -109,7 +109,7 @@ export class ViewService {
private switchPanel(uri?: URI) {
const uiBuilderPanel = this.widgetManager.getWidgetFromURI(UI_BUILDER_URI);
if (uri && UI_BUILDER_URI.match(uri)) {
// 跳转到 UIBuilder
// Jump to UIBuilder
(this.shell.mainPanel.parent?.parent as BoxPanel).hide();
uiBuilderPanel?.show();
} else {
@@ -161,7 +161,7 @@ export class ViewService {
this.switchPanel();
}
}
// 打开默认页
// Open default page
async openDefault() {
await this.openerService.open(MAIN_PANEL_DEFAULT_URI, {
mode: 'single-document',
@@ -184,8 +184,8 @@ export class ViewService {
return undefined;
}
// 由于最大分屏数量为 2
// 因此 children[0] 为左边分屏,children[1] 为右边分屏
// Since the maximum number of split screens is 2
// Therefore, children [0] is the left split screen, and children [1] is the right split screen
splitScreen(direction: 'left' | 'right', widget: ReactWidget) {
const mode = direction === 'left' ? 'split-left' : 'split-right';
const splitScreenIdx = direction === 'left' ? 0 : 1;
@@ -193,7 +193,7 @@ export class ViewService {
const layoutConfig = (
this.shell.mainPanel?.layout as DockLayout
)?.saveLayout()?.main;
// 未分屏场景,直接打开
// No split-screen scene, open it directly
if ((layoutConfig as DockLayout.ITabAreaConfig)?.type === 'tab-area') {
this.shell.mainPanel.addWidget(widget, {
mode,
@@ -205,7 +205,7 @@ export class ViewService {
const { widgets } = (layoutConfig as DockLayout.ISplitAreaConfig)
?.children[splitScreenIdx] as DockLayout.ITabAreaConfig;
const tabActivateWidget = widgets.find(_widget => _widget.isVisible);
// 已分屏场景
// split screen scene
this.shell.mainPanel.addWidget(widget, {
mode: 'tab-after',
ref: tabActivateWidget,
@@ -215,7 +215,7 @@ export class ViewService {
}
/**
* 全屏模式切换
* full screen mode toggle
*/
switchFullScreenMode() {
if (!this.isFullScreenMode) {
@@ -226,16 +226,16 @@ export class ViewService {
}
/**
* 开启全屏模式
* CozeProjectIDE 中,全屏模式隐藏侧边栏和顶部导航栏
* Enable full screen mode
* In CozeProject IDE, full-screen mode hides the sidebar and top navigation bar
*/
enableFullScreenMode() {
if (this.isFullScreenMode) {
return;
}
// 隐藏侧边栏
// Hide Sidebar
this.primarySidebar.changeVisible(false);
// 隐藏顶部导航栏
// Hide top navigation bar
const topBar = this.shell.getPanelFromArea(LayoutPanelType.TOP_BAR);
topBar.hide();
@@ -247,9 +247,9 @@ export class ViewService {
if (!this.isFullScreenMode) {
return;
}
// 显示侧边栏
// Show Sidebar
this.primarySidebar.changeVisible(true);
// 显示顶部导航栏
// Show top navigation bar
const topBar = this.shell.getPanelFromArea(LayoutPanelType.TOP_BAR);
topBar.show();

View File

@@ -60,7 +60,7 @@ export class WidgetService {
this.setTitle(this._title);
}
/** 触发重渲染 */
/** Trigger rerendering */
update() {
(this.widget.title as CustomTitleType).iconLabel = this._widgetTitleRender({
commandRegistry: this.commandRegistry,

View File

@@ -42,7 +42,7 @@ export const useLifeCycle = (
contextKeyService.setContext('widgetContext', widgetContext);
}, [widgetContext]);
const shell = useIDEService<ApplicationShell>(ApplicationShell);
// 生命周期管理
// Life Cycle Management
useEffect(() => {
const currentUri = (shell.mainPanel.currentTitle?.owner as ProjectIDEWidget)
?.uri;

View File

@@ -15,7 +15,7 @@
*/
/**
* 控制组件真正的挂载时机
* True mount timing of cgroup pieces
*/
import {
useEffect,
@@ -36,21 +36,21 @@ export const useMount = (
widget: ProjectIDEWidget,
) => {
/**
* 是否已经挂载
* Is it already mounted?
*/
const [mounted, setMounted] = useState(widget.isVisible);
const [version, setVersion] = useState(0);
const mountedRef = useRef(widget.isVisible);
/**
* 是否已加载完成
* Is it loaded?
*/
const [loaded, setLoaded] = useState(!registry.load);
/**
* renderContent 函数结果缓存
* 由于 registry widget 基本不变,可以保证在同一个 widget 中 renderContent 函数只会运行一次
* 除非 WidgetComp 组件被卸载 =.=
* renderContent function result cache
* Since the registry and widget are essentially unchanged, it is guaranteed that the renderContent function will only run once within the same widget
* Unless the WidgetComp component is uninstalled =. =
*/
const content = useMemo(() => {
if (!isFunction(registry.renderContent)) {
@@ -61,7 +61,7 @@ export const useMount = (
}, [registry, widget, version]);
/**
* 支持 registry 定义加载函数
* Support registry definition load function
*/
const load = useCallback(async () => {
if (!registry.load || !isFunction(registry.load)) {
@@ -72,7 +72,7 @@ export const useMount = (
}, [registry, widget, setLoaded]);
/**
* 监听 widget 的显示隐藏状态,若 widget 显示且未挂载,则需要主动挂载一次
* Monitor the display hidden state of the widget. If the widget is displayed and not mounted, you need to actively mount it once.
*/
const watchWidgetStatus = useCallback(
(w: ProjectIDEWidget) => {
@@ -92,7 +92,7 @@ export const useMount = (
);
/**
* 监听器可以较早挂载,避免多渲染一次
* Listeners can be mounted earlier to avoid multiple renders
*/
useLayoutEffect(() => {
const dispose = watchWidgetStatus(widget);
@@ -106,7 +106,7 @@ export const useMount = (
}, [widget, watchWidgetStatus]);
/**
* 加载函数时机暂无特殊设计,先保持和历史逻辑一致
* There is no special design for loading function timing, so keep it consistent with historical logic
*/
useEffect(() => {
load();

View File

@@ -23,14 +23,14 @@ export interface MessageEvent<T = any> {
}
/**
* widget 的通信服务
* Widget communication service
*/
@injectable()
export class MessageEventService {
@inject(WidgetManager) widgetManager: WidgetManager;
/**
* 消息队列
* message queue
*/
events = new Map<string, MessageEvent[]>();
@@ -38,11 +38,11 @@ export class MessageEventService {
onSend = this.onSendEmitter.event;
private toKey(uri: URI) {
// 通过 uri 获取 widget 的唯一索引
// Get the widget's unique index through URI
return this.widgetManager.uriToWidgetID(uri);
}
/** 通过 uri 获取消息队列 */
/** Get message queue by URI */
private get(uri: URI): MessageEvent[] {
const key = this.toKey(uri);
if (this.events.has(key)) {

View File

@@ -37,7 +37,7 @@ export enum ModalType {
interface EmitterProps {
type: ModalType;
options?: any;
// 不传默认打开窗口
// Open the window by default without sending it.
visible?: boolean;
scene?: ResourceCopyScene;
resourceName?: string;
@@ -46,10 +46,10 @@ interface EmitterProps {
const POLLING_DELAY = 1000;
/**
* 弹窗轮询服务
* startPolling 开始轮询
* retry 重试
* onCloseResourceModal 关闭轮询弹窗
* pop-up polling service
* getPolling Start polling
* Retry Retry
* onCloseResourceModal close the polling window
*/
@injectable()
export class ModalService {
@@ -71,14 +71,14 @@ export class ModalService {
protected _stopPolling = false;
private _taskId?: string;
// 开始轮询
// Start polling
async startPolling(props: ResourceCopyDispatchRequest) {
this._stopPolling = false;
const resourceName = props.res_name;
try {
// 1. 请求接口,获取 taskId
// 1. Request interface, get taskId
this.onModalVisibleChangeEmitter.fire({
type: ModalType.RESOURCE,
scene: props.scene,
@@ -90,7 +90,7 @@ export class ModalService {
if (failed_reasons?.length) {
let errorInfo = '';
// workflow 特定文案
// Workflow specific copy
if (
failed_reasons.some(reason => reason.res_type === ResType.Workflow)
) {
@@ -109,7 +109,7 @@ export class ModalService {
return;
}
// 2. 轮询接口,根据 taskId 获取任务状态
// 2. Poll the interface to get the task status according to taskId
if (task_id) {
this.doPolling(task_id);
} else {
@@ -123,12 +123,12 @@ export class ModalService {
async retry() {
this._stopPolling = false;
if (this._taskId) {
// 1. retry 接口
// 1. retry interface
await PluginDevelopApi.ResourceCopyRetry({
task_id: this._taskId,
});
this.onErrorEmitter.fire(false);
// 2. 开始轮询
// Step 2 Start polling
this.doPolling(this._taskId);
}
}
@@ -144,7 +144,7 @@ export class ModalService {
await sleep(POLLING_DELAY);
if (this._taskId && !this._stopPolling) {
// 更新弹窗内 info 信息
// Update the info information in the pop-up window
this.onModalVisibleChangeEmitter.fire({
type: ModalType.RESOURCE,
scene: taskInfo?.scene,
@@ -170,7 +170,7 @@ export class ModalService {
}
/**
* 轮询请求接口,返回轮询状态
* Polling request interface, return polling status
*/
private async polling(): Promise<ResourceCopyTaskDetail | undefined> {
try {
@@ -190,14 +190,14 @@ export class ModalService {
async onCloseResourceModal() {
this._stopPolling = true;
// 关闭 modal
// Close modal
this.onModalVisibleChangeEmitter.fire({
type: ModalType.RESOURCE,
visible: false,
});
this.onCancelEmitter.fire();
if (this._taskId) {
// 停止轮询的请求
// Request to stop polling
await PluginDevelopApi.ResourceCopyCancel({
task_id: this._taskId,
});

View File

@@ -43,45 +43,45 @@ export type WidgetTitleRender = (
export interface ProjectIDEClientProps {
view: {
/**
* 主编辑区域渲染内容
* Main editing area rendering content
*/
widgetRegistries: WidgetRegistry[];
/**
* 默认渲染页
* default render page
*/
widgetDefaultRender: () => ReactElement<any, any>;
/**
* widget 兜底报错,如果 widget 挂掉会渲染该组件,发送埋点
* The widget reports an error. If the widget hangs up, it will render the component and send event tracking.
*/
widgetFallbackRender?: (props: {
widget: ReactWidget;
}) => ReactElement<any, any>;
/**
* 统一标题渲染
* unified title rendering
*/
widgetTitleRender: WidgetTitleRender;
/**
* 主侧边栏渲染
* Primary sidebar rendering
*/
primarySideBar: () => ReactElement<any, any>;
/**
* 辅助侧边栏渲染
* Auxiliary Sidebar Rendering
*/
secondarySidebar?: () => ReactElement<any, any>;
/**
* 主侧边栏底部分区 configuration 配置渲染
* Main sidebar bottom partition configuration rendering
*/
configuration?: () => ReactElement<any, any>;
/**
* 前置工具栏渲染
* Front toolbar rendering
*/
preToolbar?: () => ReactElement<any, any>;
/**
* 后置工具栏渲染
* Rear toolbar rendering
*/
toolbar?: (widget: ProjectIDEWidget) => ReactElement<any, any>;
/**
* 顶部导航栏
* Top navigation bar
*/
topBar: () => ReactElement<any, any>;
/**

View File

@@ -25,14 +25,14 @@ import { type WidgetContext } from '@/context/widget-context';
import { type CommandItem, type MenuItem, type ShortcutItem } from './services';
export interface WidgetRegistry<T = any> {
// widget 渲染区域
// Widget rendering area
area?: LayoutPanelType;
// 规则匹配
// rule matching
match: RegExp;
canClose?: () => boolean;
// 数据存储
// data storage
createStore?: (uri?: URI) => T;
// 注册
// Register
registerCommands?: () => CommandItem<T>[];
registerShortcuts?: () => ShortcutItem[];
registerContextMenu?: () => MenuItem[];
@@ -43,15 +43,15 @@ export interface WidgetRegistry<T = any> {
widget?: ReactWidget,
) => React.ReactElement<any, any>;
// 生命周期
// Life Cycle
load?: (ctx: WidgetContext<T>) => Promise<void>;
/**
* 注意:分屏场景,如果有一个面板之前未展示,会先 focus 那个面板,然后 focus 当前选中的面板。
* Note: For split-screen scenes, if there is a panel that has not been displayed before, it will focus that panel first, and then focus the currently selected panel.
*/
onFocus?: (ctx: WidgetContext<T>) => void;
/**
* 业务侧销毁逻辑
* createStore 的销毁逻辑由业务侧自行处理
* Business side destruction logic
* The destruction logic of createStore is handled by the business side itself
*/
onDispose?: (ctx: WidgetContext<T>) => void;
}

View File

@@ -15,10 +15,10 @@
*/
/**
* 资源类型
* resource type
*/
export type ResourceType = 'workflow' | 'knowledge' | 'ui-builder';
/**
* 模式类型
* schema type
*/
export type ModeType = 'dev' | 'ui-builder';

View File

@@ -26,42 +26,42 @@ export interface CommandItem<T> {
}
export interface ShortcutItem {
// 命令系统中绑定的 id
// ID bound in the command system
commandId: string;
// 快捷键
// shortcut
keybinding: string;
// 是否阻止浏览器原生行为
// Whether to block browser native behavior
preventDefault: boolean;
}
export interface CommandService {
execute: (id: string, ...args: any[]) => void; // 执行命令
execute: (id: string, ...args: any[]) => void; // execute the command
}
export interface MenuItem {
/**
* 使用已经注册的 command 的 id
* Use the id of a registered command
*/
commandId: string;
/**
* 元素选择器
* 类:.class
* element selector
* Class:. class
* id#id
*/
selector: string;
/**
* 子菜单
* submenu
*/
submenu?: MenuItem[];
}
export interface ContextMenuService {
open: (e: React.MouseEvent) => boolean; // 没有任何菜单注册项,返回 false
registerContextMenu: (options: MenuItem[], match?: RegExp) => void; // 入参形同 widgetRegistry 里的 registerContextMenu
open: (e: React.MouseEvent) => boolean; // There are no menu registration items, return false
registerContextMenu: (options: MenuItem[], match?: RegExp) => void; // Imported parameters are like registerContextMenu in widgetRegistry
}
export interface ProjectIDEServices {
contextmenu: ContextMenuService; // 右键菜单服务
command: CommandService; // 命令服务
contextmenu: ContextMenuService; // Right-click menu service
command: CommandService; // command service
view: ViewService;
}

View File

@@ -14,12 +14,12 @@
* limitations under the License.
*/
/** 保留的查询参数 */
/** reserved query parameters */
const PRESERVED_SEARCH_PARAMS = ['commit_version'];
/**
* 给指定 url 添加特定的 search params
* @param url 当前 url
* Add specific search params to the specified URL
* @param url current url
* @returns
*/
export function addPreservedSearchParams(url: string) {

View File

@@ -36,7 +36,7 @@ export const customLayout = (
const leftRightSplitLayout = createBoxLayout(
[
// 左边的不可伸缩 bar
// Unretractable bar on the left
shell.primarySidebar,
middleContentPanel,
],

View File

@@ -19,7 +19,7 @@ import { URI } from '@coze-project-ide/client';
import { URI_SCHEME } from '../constants';
/**
* 从给定的 url 字符串中解析出 resourceType resourceId;
* Resolve the resourceType and resourceId from the given url string;
*/
export const getResourceByPathname = (pathname: string) => {
let resourceType: undefined | string;
@@ -45,11 +45,11 @@ export const getURIPathByPathname = (pathname: string) => {
};
/**
* 从 uri 上解析 resourceType resourceId
* Parse resourceType and resourceId from URIs
*/
export const getResourceByURI = (uri: URI) => {
/**
* TODO: 这样解析有些粗暴了,后面要调整一下
* TODO: This analysis is a bit rough, and it will be adjusted later.
*/
const resourceType = uri.path.dir.base;
const resourceId = uri.path.base;
@@ -63,7 +63,7 @@ export const getResourceByURI = (uri: URI) => {
export const getPathnameByURI = (uri: URI) => uri.path.toString();
/**
* 根据 resourceType resourceId 生成 URI
* Generate URIs from resourceType and resourceId
*/
export const getURIByResource = (
resourceType: string,
@@ -78,7 +78,7 @@ export const getURIByPath = (path: string) =>
new URI(`${URI_SCHEME}:///${path}`);
/**
* 将 uri 转化为 url
* Convert URI to URL
*/
export const getURLByURI = (uri: URI) =>
`${uri.path.toString()}${uri.query ? `${uri.query}` : ''}${
@@ -86,7 +86,7 @@ export const getURLByURI = (uri: URI) =>
}`;
/**
* 执行 URI 比对,完全一致返回 true否则返回 false
* Perform URI alignment, return true exactly, otherwise return false
*/
export const compareURI = (uri1?: URI, uri2?: URI) => {
if (!uri1 || !uri2) {