chore: replace all cn comments of fe to en version by volc api (#320)
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 插件配置
|
||||
* plug-in configuration
|
||||
*/
|
||||
import { type NavigateFunction } from 'react-router-dom';
|
||||
|
||||
|
||||
@@ -31,18 +31,18 @@ interface CreateStoreOptions {
|
||||
|
||||
export interface IDEGlobalState {
|
||||
/**
|
||||
* 项目 id
|
||||
* Project ID
|
||||
*/
|
||||
projectId: string;
|
||||
/**
|
||||
* 空间 id
|
||||
* Space ID
|
||||
*/
|
||||
spaceId: string;
|
||||
|
||||
version: string;
|
||||
|
||||
/**
|
||||
* get_draft_intelligence_info 接口返回内容
|
||||
* get_draft_intelligence_info interface returns content
|
||||
*/
|
||||
projectInfo?: {
|
||||
ownerInfo?: User;
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface WsMessageProps {
|
||||
resId: string;
|
||||
extra: any;
|
||||
/**
|
||||
* 其他窗口执行保存传入的版本号
|
||||
* Other window execution saves the incoming version number
|
||||
*/
|
||||
saveVersion?: string;
|
||||
operateType: MessageOperateType;
|
||||
|
||||
@@ -39,7 +39,7 @@ export const iconFolderOpended = (
|
||||
|
||||
export const VARIABLE_RESOURCE_ID = 'variables';
|
||||
|
||||
/* 资源文件名最大字符数 */
|
||||
/* Maximum number of characters for a resource file name */
|
||||
export const RESOURCE_NAME_MAX_LEN = 50;
|
||||
export const resourceIconMap = {
|
||||
[BizResourceTypeEnum.Workflow]: <IconCozWorkflow />,
|
||||
@@ -102,5 +102,5 @@ export const contextMenuDTOToVOMap = {
|
||||
export const MAX_DEEP = 6;
|
||||
export const TAB_SIZE = 14;
|
||||
export const ITEM_HEIGHT = 28;
|
||||
/* 禁用文件夹功能 */
|
||||
/* Disable folder feature */
|
||||
export const DISABLE_FOLDER = true;
|
||||
|
||||
@@ -79,7 +79,7 @@ export type UseResourceFolderConfigProps = {
|
||||
) => void;
|
||||
createResourceConfig?: ResourceFolderCozeProps['createResourceConfig'];
|
||||
/**
|
||||
* 隐藏更多菜单按钮
|
||||
* Hide more menu button
|
||||
*/
|
||||
hideMoreBtn?: boolean;
|
||||
} & Pick<ResourceFolderProps, 'validateConfig'>;
|
||||
@@ -117,7 +117,7 @@ export const useResourceFolderConfig = ({
|
||||
return [];
|
||||
}
|
||||
if (resource.id === ROOT_KEY) {
|
||||
// 新建文件夹,新建文件,引入资源库
|
||||
// New folder, new file, import resource library
|
||||
const createHandlers = createResourceConfig
|
||||
? createResourceConfig.map(({ label, subType }) => ({
|
||||
id: `${BizResourceContextMenuBtnType.CreateResource}-${subType}`,
|
||||
|
||||
@@ -45,7 +45,7 @@ export class ResourceFolderContribution
|
||||
}
|
||||
|
||||
registerShortcuts(registry: ShortcutsRegistry): void {
|
||||
// 重命名
|
||||
// rename
|
||||
registry.registerHandlers({
|
||||
commandId: BizResourceContextMenuBtnType.Rename,
|
||||
keybinding: 'enter',
|
||||
@@ -54,7 +54,7 @@ export class ResourceFolderContribution
|
||||
when: RESOURCE_FOLDER_CONTEXT_KEY,
|
||||
});
|
||||
|
||||
// 删除
|
||||
// delete
|
||||
registry.registerHandlers({
|
||||
commandId: BizResourceContextMenuBtnType.Delete,
|
||||
keybinding: 'meta backspace',
|
||||
@@ -63,7 +63,7 @@ export class ResourceFolderContribution
|
||||
when: RESOURCE_FOLDER_CONTEXT_KEY,
|
||||
});
|
||||
|
||||
// 创建文件夹
|
||||
// Create Folder
|
||||
registry.registerHandlers({
|
||||
commandId: BizResourceContextMenuBtnType.CreateFolder,
|
||||
keybinding: 'alt shift n',
|
||||
@@ -72,7 +72,7 @@ export class ResourceFolderContribution
|
||||
when: RESOURCE_FOLDER_CONTEXT_KEY,
|
||||
});
|
||||
|
||||
// 创建资源
|
||||
// Create a resource
|
||||
registry.registerHandlers({
|
||||
commandId: BizResourceContextMenuBtnType.CreateResource,
|
||||
keybinding: 'alt n',
|
||||
@@ -81,7 +81,7 @@ export class ResourceFolderContribution
|
||||
when: RESOURCE_FOLDER_CONTEXT_KEY,
|
||||
});
|
||||
|
||||
// 创建副本
|
||||
// Create a copy
|
||||
registry.registerHandlers({
|
||||
commandId: BizResourceContextMenuBtnType.DuplicateResource,
|
||||
keybinding: 'alt d',
|
||||
@@ -91,7 +91,7 @@ export class ResourceFolderContribution
|
||||
});
|
||||
}
|
||||
registerCommands(commands: CommandRegistry): void {
|
||||
// 重命名 command
|
||||
// Rename command
|
||||
commands.registerCommand(
|
||||
{
|
||||
id: BizResourceContextMenuBtnType.Rename,
|
||||
@@ -105,7 +105,7 @@ export class ResourceFolderContribution
|
||||
},
|
||||
);
|
||||
|
||||
// 删除 command
|
||||
// Delete command
|
||||
commands.registerCommand(
|
||||
{
|
||||
id: BizResourceContextMenuBtnType.Delete,
|
||||
@@ -117,7 +117,7 @@ export class ResourceFolderContribution
|
||||
},
|
||||
);
|
||||
|
||||
// 新建文件夹 command
|
||||
// New folder command
|
||||
commands.registerCommand(
|
||||
{
|
||||
id: BizResourceContextMenuBtnType.CreateFolder,
|
||||
@@ -131,13 +131,13 @@ export class ResourceFolderContribution
|
||||
);
|
||||
resourceFolderDispatch?.onCreateFolder?.();
|
||||
},
|
||||
// 禁用文件夹创建
|
||||
// Disable folder creation
|
||||
isEnabled: opt => false, //!opt?.disabled,
|
||||
isVisible: opt => !opt?.isHidden,
|
||||
},
|
||||
);
|
||||
|
||||
// 新建资源 command
|
||||
// New resource command
|
||||
commands.registerCommand(
|
||||
{
|
||||
id: BizResourceContextMenuBtnType.CreateResource,
|
||||
@@ -150,7 +150,7 @@ export class ResourceFolderContribution
|
||||
isVisible: opt => !opt?.isHidden,
|
||||
},
|
||||
);
|
||||
// 新建副本 command
|
||||
// New replica command
|
||||
commands.registerCommand(
|
||||
{
|
||||
id: BizResourceContextMenuBtnType.DuplicateResource,
|
||||
|
||||
@@ -122,11 +122,11 @@ const ResourceFolderCozeImpl: FC<ResourceFolderCozeProps> = ({
|
||||
if (event.id !== props.id) {
|
||||
return;
|
||||
}
|
||||
// 多个创建资源菜单,不使用快捷键
|
||||
// Multiple Create Resource menus without shortcuts
|
||||
if (createResourceConfig) {
|
||||
return;
|
||||
}
|
||||
// 快捷键触发的创建资源
|
||||
// Shortcut-triggered creation of resources
|
||||
handleCreateResource(groupType);
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
} from '@coze-project-ide/framework';
|
||||
|
||||
/**
|
||||
* 用于 ResourceType['type'] 里指定资源类型
|
||||
* Used to specify the resource type in ResourceType ['type']
|
||||
*/
|
||||
export enum BizResourceTypeEnum {
|
||||
Workflow = 'workflow',
|
||||
@@ -44,33 +44,33 @@ export interface BizResourceTree {
|
||||
}
|
||||
|
||||
export enum BizResourceContextMenuBtnType {
|
||||
/* 创建资源 */
|
||||
/* Create a resource */
|
||||
CreateResource = 'resource-folder-create-resource',
|
||||
/* 创建文件夹 */
|
||||
/* Create Folder */
|
||||
CreateFolder = 'resource-folder-create-folder',
|
||||
/* 重命名 */
|
||||
/* rename */
|
||||
Rename = 'resource-folder-rename',
|
||||
/* 删除 */
|
||||
/* delete */
|
||||
Delete = 'resource-folder-delete',
|
||||
|
||||
/* ----------------- 下面的事件会通过 onAction 进行回调 ----------------- */
|
||||
/* 创建副本 */
|
||||
/* ----------------- the following events are called back with onAction ----------------- */
|
||||
/* Create a copy */
|
||||
DuplicateResource = 'resource-folder-duplicate-resource',
|
||||
/* 引入资源库文件 */
|
||||
/* Import repository file */
|
||||
ImportLibraryResource = 'resource-folder-import-library-resource',
|
||||
/* 移动到资源库 */
|
||||
/* Move to Library */
|
||||
MoveToLibrary = 'resource-folder-move-to-library',
|
||||
/* 复制到资源库 */
|
||||
/* Copy to repository */
|
||||
CopyToLibrary = 'resource-folder-copy-to-library',
|
||||
/* 启用知识库 */
|
||||
/* Enable Knowledge Base */
|
||||
EnableKnowledge = 'resource-folder-enable-knowledge',
|
||||
/* 禁用知识库 */
|
||||
/* Disable Knowledge Base */
|
||||
DisableKnowledge = 'resource-folder-disable-knowledge',
|
||||
/* 切换为 chatflow */
|
||||
/* Switch to chatflow */
|
||||
SwitchToChatflow = 'resource-folder-switch-to-chatflow',
|
||||
/* 切换为 workflow */
|
||||
/* Switch to workflow */
|
||||
SwitchToWorkflow = 'resource-folder-switch-to-workflow',
|
||||
/* 修改描述 */
|
||||
/* Modify description */
|
||||
UpdateDesc = 'resource-folder-update-desc',
|
||||
}
|
||||
|
||||
@@ -87,46 +87,46 @@ export type BizResourceType = ResourceType & ProjectResourceInfo;
|
||||
export type ResourceSubType = string | number;
|
||||
|
||||
export type ResourceFolderCozeProps = {
|
||||
/** 资源类型 */
|
||||
/** resource type */
|
||||
groupType: ProjectResourceGroupType;
|
||||
/** 后端资源列表数据 */
|
||||
/** backend resource list data */
|
||||
resourceTree: BizResourceType[];
|
||||
/** 自定义事件回调,点击右键菜单/上下文菜单会触发,由业务方实现自定义事件的资源更新逻辑逻辑 */
|
||||
/** Custom event callback, click the right-click menu/context menu will trigger, and the business party will implement the resource update logic of the custom event */
|
||||
onAction?: (
|
||||
action: BizResourceContextMenuBtnType,
|
||||
resource?: BizResourceType,
|
||||
) => void;
|
||||
/**
|
||||
* 自定义资源创建流程时使用这个 props,需要业务自己展示资源创建表单,然后更新 resource
|
||||
* 如果没有自定义逻辑,使用 onCreate
|
||||
* This props is used when creating a custom resource definition process. The business needs to display the resource creation form by itself, and then update the resource.
|
||||
* If there is no custom logic, use onCreate
|
||||
*/
|
||||
onCustomCreate?: (
|
||||
groupType: ProjectResourceGroupType,
|
||||
subType?: ResourceSubType,
|
||||
) => void;
|
||||
/**
|
||||
* 使用默认创建资源的方式,创建资源后的回调,在这里调用业务的创建资源接口
|
||||
* 配置了 onCustomCreate 会使 onCreate 失效
|
||||
* 默认创建资源方式:点击新建菜单后,资源列表增加新资源 input 输入框,输入资源名称回车完成创建,触发 onCreate 回调
|
||||
* Use the default way of creating resources, the callback after creating resources, and call the create resource interface of the business here
|
||||
* Configuring onCustomCreate will invalidate onCreate
|
||||
* Default resource creation method: After clicking the New menu, add a new resource to the resource list input text box, enter the resource name Enter to complete the creation, and trigger the onCreate callback
|
||||
* @param createEvent
|
||||
* @param subType 如果配置了 createResourceConfig 数组,会有多个创建资源的菜单,通过 subType 区分子类型
|
||||
* @Param subType If createResourceConfig array is configured, there will be multiple menus to create resources, distinguish subtypes by subType
|
||||
*/
|
||||
onCreate?: (
|
||||
createEvent: CreateResourcePropType,
|
||||
subType?: ResourceSubType,
|
||||
) => void;
|
||||
/**
|
||||
* 是否可以创建资源
|
||||
* Is it possible to create resources?
|
||||
*/
|
||||
canCreate?: boolean;
|
||||
/**
|
||||
* 完成初始加载,用于判断显示空状态
|
||||
* Complete the initial loading, used to determine the display empty state
|
||||
*/
|
||||
initLoaded?: boolean;
|
||||
/**
|
||||
* 创建资源的 UI 配置
|
||||
* 如果资源有多个子类型,可以配置为数组
|
||||
* 触发 onCreateResource 会传第二个参数 subType
|
||||
* Create UI configurations for resources
|
||||
* If the resource has multiple subtypes, it can be configured as an array
|
||||
* Triggering onCreateResource will pass the second parameter subType
|
||||
*/
|
||||
createResourceConfig?: Array<{
|
||||
icon: ReactNode;
|
||||
@@ -135,11 +135,11 @@ export type ResourceFolderCozeProps = {
|
||||
tooltip: ReactNode;
|
||||
}>;
|
||||
/**
|
||||
* 主要用于快捷键创建资源的默认类型。
|
||||
* Mainly used to shortcut the default type for creating resources.
|
||||
*/
|
||||
defaultResourceType?: BizResourceTypeEnum;
|
||||
/**
|
||||
* 隐藏更多菜单按钮
|
||||
* Hide more menu button
|
||||
*/
|
||||
hideMoreBtn?: boolean;
|
||||
} & Pick<
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
import { iconFolder, iconFolderOpended, resourceIconMap } from './constants';
|
||||
|
||||
export const validateNameBasic: Validator = ({ label }) => {
|
||||
// 检测 name 是否空
|
||||
// Check if name is empty
|
||||
if (!label) {
|
||||
return I18n.t('project_resource_sidebar_warning_empty_key');
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export const validateNameBasic: Validator = ({ label }) => {
|
||||
return I18n.t('project_resource_sidebar_warning_length_exceeds');
|
||||
}
|
||||
|
||||
// 检测 name 的命名规则
|
||||
// Detection of naming rules for names
|
||||
if (!WORKFLOW_NAME_REGEX.test(label)) {
|
||||
return I18n.t('workflow_list_create_modal_name_rule_reg');
|
||||
}
|
||||
@@ -164,7 +164,7 @@ export const getContextMenuLabel = (
|
||||
};
|
||||
|
||||
/**
|
||||
* 是否动态注册的 action
|
||||
* Whether the action is dynamically registered
|
||||
* @param action
|
||||
*/
|
||||
export const isDynamicAction = (action: BizResourceContextMenuBtnType) =>
|
||||
|
||||
@@ -31,13 +31,13 @@ export interface PrimarySidebarActions {
|
||||
initLoaded?: boolean;
|
||||
setSelectedResource: (resourceId?: string) => void;
|
||||
/**
|
||||
* 调用 store 里的 refetch 方法通知资源列表刷新
|
||||
* @param resourceType 刷新的资源类型,可选,不传时刷新所有类型的资源
|
||||
* Call the refetch method in the store to notify the resource list to refresh
|
||||
* @Param resourceType The type of resource to refresh, optional, refresh all types of resources when not passing
|
||||
*/
|
||||
refetch: (callback?: (tree?: BizResourceTree[]) => void) => Promise<void>;
|
||||
/**
|
||||
* 设置资源列表的刷新方法到 store 里,供其他 widget 里调用 store.refetch(xxx) 刷新资源列表
|
||||
* @param refetchFunc 刷新方法,刷新方法里内部需要处理资源列表的 setResource
|
||||
* Set the refresh method of the resource list to the store, and call store.refetch (xxx) in other widgets to refresh the resource list
|
||||
* @Param refetchFunc refresh method, refresh the setResource in the method that needs to process the resource list internally
|
||||
*/
|
||||
fetchResource: (
|
||||
spaceId: string,
|
||||
@@ -54,15 +54,15 @@ export interface PrimarySidebarState {
|
||||
version?: string;
|
||||
resourceTree: BizResourceTree[];
|
||||
/**
|
||||
* 资源分组展开状态,按资源分组类型分开记录
|
||||
* Resource packet expansion status, recorded separately by resource packet type
|
||||
*/
|
||||
groupExpandMap: Record<ProjectResourceGroupType, boolean>;
|
||||
/**
|
||||
* 当前选中的资源
|
||||
* The currently selected resource
|
||||
*/
|
||||
selectedResource?: string;
|
||||
/**
|
||||
* 是否可以关闭 popover sidebar,右键菜单打开时不允许关闭,供 ResourceFolderCoze 组件消费
|
||||
* Can you close the popover sidebar? It is not allowed to close when the right-click menu is opened, for consumption by the ResourceFolderCoze component
|
||||
*/
|
||||
canClosePopover?: boolean;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ export const usePrimarySidebarStore = create<
|
||||
if (!spaceId || !projectId) {
|
||||
return;
|
||||
}
|
||||
// 服务端数据同步延迟
|
||||
// Server level data synchronization delay
|
||||
await new Promise<void>(resolve => setTimeout(() => resolve(), 700));
|
||||
return get().fetchResource(spaceId, projectId, version, callback);
|
||||
},
|
||||
|
||||
@@ -66,9 +66,9 @@ const Main = () => {
|
||||
agentID: agent_id,
|
||||
}}
|
||||
onUpdateDisplayName={displayName => {
|
||||
widget.setTitle(displayName); // 设置 tab 标题
|
||||
widget.setTitle(displayName); // Set tab title
|
||||
if (displayName && displayName !== title) {
|
||||
refetch(); // 更新侧边栏 name
|
||||
refetch(); // Update sidebar name
|
||||
}
|
||||
}}
|
||||
onStatusChange={status => {
|
||||
|
||||
@@ -58,7 +58,7 @@ const useDatabaseResource = (): UseDatabaseResourceReturn => {
|
||||
const IDENav = useIDENavigate();
|
||||
// const openResource = useOpenResource();
|
||||
|
||||
// 创建 Database
|
||||
// Create Database
|
||||
const {
|
||||
modal: createDatabaseModal,
|
||||
open: openCreateDatabaseModal,
|
||||
|
||||
@@ -59,7 +59,7 @@ const useKnowledgeResource = (): UseKnowledgeResourceReturn => {
|
||||
const IDENav = useIDENavigate();
|
||||
// const openResource = useOpenResource();
|
||||
|
||||
// 创建knowledge
|
||||
// Creating knowledge
|
||||
const {
|
||||
modal: createKnowledgeModal,
|
||||
open: openCreateKnowledgeModal,
|
||||
@@ -75,7 +75,7 @@ const useKnowledgeResource = (): UseKnowledgeResourceReturn => {
|
||||
},
|
||||
});
|
||||
|
||||
// 更新 knowledge 状态,主要用于禁用启用
|
||||
// Update knowledge status, mainly to disable enable
|
||||
const updateKnowledge = async (
|
||||
datasetID: string,
|
||||
datasetName: string,
|
||||
|
||||
@@ -95,9 +95,9 @@ const Main = () => {
|
||||
create,
|
||||
}}
|
||||
onUpdateDisplayName={displayName => {
|
||||
widget.setTitle(displayName); // 设置 tab 标题
|
||||
widget.setTitle(displayName); // Set tab title
|
||||
if (displayName && displayName !== title) {
|
||||
refetch(); // 更新侧边栏 name
|
||||
refetch(); // Update sidebar name
|
||||
}
|
||||
}}
|
||||
onStatusChange={status => {
|
||||
|
||||
@@ -191,7 +191,7 @@ const usePluginResource = (): UsePluginResourceReturn => {
|
||||
}
|
||||
};
|
||||
const validateNameBasic: Validator = ({ label }) => {
|
||||
// 检测 name 是否空
|
||||
// Check if name is empty
|
||||
if (!label) {
|
||||
return I18n.t('create_plugin_modal_name1_error');
|
||||
}
|
||||
@@ -200,7 +200,7 @@ const usePluginResource = (): UsePluginResourceReturn => {
|
||||
return I18n.t('project_resource_sidebar_warning_length_exceeds');
|
||||
}
|
||||
|
||||
// 检测 name 的命名规则,国内增加支持中文
|
||||
// Detect the naming rules for names, and add support for Chinese in China
|
||||
if (IS_OVERSEA || IS_BOE) {
|
||||
if (!/^[\w\s]+$/.test(label)) {
|
||||
return I18n.t('create_plugin_modal_nameerror');
|
||||
|
||||
@@ -55,7 +55,7 @@ interface PluginProviderContentProps extends MainProps {
|
||||
version: string;
|
||||
}
|
||||
|
||||
// 新增的 PluginProviderContent 组件
|
||||
// Added PluginProviderContent Components
|
||||
const PluginProviderContent: React.FC<PluginProviderContentProps> = ({
|
||||
pluginID,
|
||||
spaceID,
|
||||
@@ -153,9 +153,9 @@ const Main: React.FC<MainProps> = props => {
|
||||
projectID={projectID}
|
||||
version={version}
|
||||
onUpdateDisplayName={displayName => {
|
||||
widget.setTitle(displayName); // 设置 tab 标题
|
||||
widget.setTitle(displayName); // Set tab title
|
||||
if (displayName && displayName !== title) {
|
||||
refetch(); // 更新侧边栏 name
|
||||
refetch(); // Update sidebar name
|
||||
}
|
||||
}}
|
||||
onStatusChange={status => {
|
||||
|
||||
@@ -34,19 +34,19 @@ const LazyBuilderChat = lazy(async () => {
|
||||
|
||||
export interface ChatHistoryProps {
|
||||
/**
|
||||
* 会话 id
|
||||
* session id
|
||||
*/
|
||||
conversationId?: string;
|
||||
/**
|
||||
* 会话名称
|
||||
* session name
|
||||
*/
|
||||
conversationName: string;
|
||||
/**
|
||||
* 渠道 id
|
||||
* Channel ID
|
||||
*/
|
||||
connectorId: string;
|
||||
/**
|
||||
* 创建会话的环境
|
||||
* Create a conversation environment
|
||||
*/
|
||||
createEnv: CreateEnv;
|
||||
}
|
||||
@@ -106,7 +106,7 @@ export const ChatHistory: React.FC<ChatHistoryProps> = ({
|
||||
workflow={{}}
|
||||
project={innerProjectInfo}
|
||||
areaUi={{
|
||||
// 只看会话记录,不可操作
|
||||
// Only look at the session record, not operate
|
||||
isDisabled: true,
|
||||
isNeedClearContext: false,
|
||||
input: {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
// 默认会话 unique_id
|
||||
// Default session unique_id
|
||||
export const DEFAULT_UNIQUE_ID = '0';
|
||||
|
||||
export const DEFAULT_CONVERSATION_NAME = 'Default';
|
||||
@@ -31,7 +31,7 @@ export enum ErrorCode {
|
||||
export const MAX_INPUT_LEN = 200;
|
||||
|
||||
/**
|
||||
* 调试的渠道 id
|
||||
* Debug channel id
|
||||
*/
|
||||
export const DEBUG_CONNECTOR_ID = '_10000010';
|
||||
|
||||
@@ -45,12 +45,12 @@ export const API_CONNECTOR_ID = '1024';
|
||||
export const CHAT_SDK_CONNECTOR_ID = '999';
|
||||
export const COZE_CONNECTOR_IDS = [COZE_CONNECTOR_ID, '10000122', '10000129'];
|
||||
/**
|
||||
* 不存在的会话
|
||||
* Conversation that does not exist
|
||||
*/
|
||||
export const DISABLED_CONVERSATION = '0';
|
||||
|
||||
/**
|
||||
* 只展示这些线上渠道,其他的后端不支持 @qiangshunliang
|
||||
* Only show these online channels, other backends do not support @qiangshunliang.
|
||||
*/
|
||||
export const ALLOW_CONNECTORS = [
|
||||
COZE_CONNECTOR_ID,
|
||||
|
||||
@@ -33,7 +33,7 @@ export const EditInput = ({
|
||||
}: {
|
||||
ref?: React.Ref<HTMLInputElement>;
|
||||
/**
|
||||
* 默认值
|
||||
* default value
|
||||
*/
|
||||
defaultValue?: string;
|
||||
/**
|
||||
@@ -41,11 +41,11 @@ export const EditInput = ({
|
||||
*/
|
||||
loading: boolean;
|
||||
/**
|
||||
* 失焦 / 回车后执行的行为
|
||||
* Behavior performed after out of focus/enter
|
||||
*/
|
||||
onBlur?: (input?: string, error?: ErrorCode) => void;
|
||||
/**
|
||||
* 校验函数,返回 true 标识校验通过
|
||||
* Verification function, returns true to indicate that the verification passed
|
||||
*/
|
||||
onValidate?: (input: string) => ErrorCode | undefined;
|
||||
}) => {
|
||||
|
||||
@@ -62,7 +62,7 @@ export const ConversationContent: React.FC<ConversationContentProps> = ({
|
||||
canEdit,
|
||||
}) => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
// 顶部的创建弹窗
|
||||
// Create pop-up window at the top
|
||||
const [inputVisible, setInputVisible] = useState(false);
|
||||
const [activateChat, setActivateChat] = useState<
|
||||
ProjectConversation | undefined
|
||||
@@ -97,7 +97,7 @@ export const ConversationContent: React.FC<ConversationContentProps> = ({
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// 初始化选中接口返回数据。conversationId 可能为 0 要展示空
|
||||
// Initialize the selected interface to return data. conversationId may be 0 to display empty
|
||||
if (!activateChat && staticList?.length) {
|
||||
handleSelectChat(staticList[0]);
|
||||
}
|
||||
@@ -155,7 +155,7 @@ export const ConversationContent: React.FC<ConversationContentProps> = ({
|
||||
) : null;
|
||||
|
||||
useEffect(() => {
|
||||
// 判断会话页是否显示,实现切换tab时刷新列表效果
|
||||
// Determine whether the session page is displayed, and achieve the effect of refreshing the list when switching tabs
|
||||
const value = getURIPathByPathname(pathname);
|
||||
if (value && CONVERSATION_URI.displayName === value) {
|
||||
fetch();
|
||||
|
||||
@@ -134,7 +134,7 @@ export const DynamicChatList = ({
|
||||
!!canEdit &&
|
||||
!!list?.length &&
|
||||
!inBatch &&
|
||||
// 即将支持,敬请期待
|
||||
// Support soon, so stay tuned.
|
||||
FLAGS['bot.automation.conversation_batch_delete'];
|
||||
const exitBatch = () => {
|
||||
setInBatch(false);
|
||||
@@ -151,7 +151,7 @@ export const DynamicChatList = ({
|
||||
const handleBatchDelete = async (items: ProjectConversation[]) => {
|
||||
const ids = items.map(i => i.unique_id).filter((i): i is string => !!i);
|
||||
await onBatchDelete(ids);
|
||||
// 删除成功后退出批量操作模式
|
||||
// Exit batch operation mode after successful deletion
|
||||
exitBatch();
|
||||
};
|
||||
return (
|
||||
|
||||
@@ -70,12 +70,12 @@ const useConversationList = (params: ListProjectConversationDefParams) => {
|
||||
export const useConversationListWithConnector = (
|
||||
params: ConversationListWithConnectorParams,
|
||||
) => {
|
||||
// 静态
|
||||
// static
|
||||
const { list: staticList, fetch: fetchStatic } = useConversationList({
|
||||
create_method: CreateMethod.ManualCreate,
|
||||
...params,
|
||||
});
|
||||
// 动态
|
||||
// dynamic
|
||||
const { list: dynamicList, fetch: fetchDynamic } = useConversationList({
|
||||
create_method: CreateMethod.NodeCreate,
|
||||
...params,
|
||||
|
||||
@@ -68,7 +68,7 @@ export const useDeleteChat = ({
|
||||
}));
|
||||
|
||||
/**
|
||||
* 给外部的 check,用作 replace 请求
|
||||
* To an external check, used as a replace request
|
||||
*/
|
||||
const handleDelete = async (_chat?: ProjectConversation) => {
|
||||
setChat(_chat);
|
||||
@@ -109,7 +109,7 @@ export const useDeleteChat = ({
|
||||
setReplace([]);
|
||||
setVisible(false);
|
||||
Toast.success(I18n.t('wf_chatflow_112'));
|
||||
// 删除成功后刷新列表
|
||||
// Refresh the list after successful deletion
|
||||
manualRefresh();
|
||||
setActivateChat(undefined);
|
||||
} else {
|
||||
|
||||
@@ -59,7 +59,7 @@ export const StaticChatList = ({
|
||||
renderCreateInput: () => React.ReactNode;
|
||||
handleCreateInput?: () => void;
|
||||
}) => {
|
||||
// 存储 session_id
|
||||
// Storage session_id
|
||||
const [editingUniqueId, setEditingUniqueId] = useState('');
|
||||
|
||||
const handleEditSession = (inputStr?: string, error?: ErrorCode) => {
|
||||
@@ -74,8 +74,8 @@ export const StaticChatList = ({
|
||||
};
|
||||
|
||||
/**
|
||||
* ux @wangwenbo.me 设计,default 放在首位,
|
||||
* 剩余的接口返回按照创建先后顺序倒序排序(后创建的放前边)
|
||||
* UX @wangwenbo.me design, default first,
|
||||
* The remaining interfaces are returned in reverse order according to the order in which they were created (the ones created later are placed first).
|
||||
*/
|
||||
return (
|
||||
<>
|
||||
@@ -153,7 +153,7 @@ export const StaticChatList = ({
|
||||
handleSessionVisible(item.unique_id);
|
||||
}}
|
||||
/>
|
||||
{/* 默认会话不可删除 */}
|
||||
{/* Default session cannot be deleted */}
|
||||
<IconButton
|
||||
size="small"
|
||||
color="secondary"
|
||||
|
||||
@@ -36,7 +36,7 @@ export const workflowActions = [
|
||||
hint: '复制到资源库',
|
||||
},
|
||||
{
|
||||
// 切换为 chatflow
|
||||
// Switch to chatflow
|
||||
key: ProjectResourceActionKey.SwitchToChatflow,
|
||||
enable: true,
|
||||
},
|
||||
|
||||
@@ -50,7 +50,7 @@ export const useChangeFlowMode = () => {
|
||||
const uri = getURIByResource('workflow', workflowId);
|
||||
const widgetContext = view.getWidgetContextFromURI(uri);
|
||||
const widgetOpened = Boolean(widgetContext?.widget);
|
||||
// 已经打开的 widget,加 refresh 参数刷新,未打开的直接打开会刷新
|
||||
// For widgets that have been opened, add the refresh parameter to refresh. If they are not opened, they will be refreshed if they are opened directly.
|
||||
navigate(`/workflow/${workflowId}${widgetOpened ? '?refresh=true' : ''}`);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ export const useNameValidators = ({
|
||||
return [
|
||||
{
|
||||
validator(_, value) {
|
||||
// 过滤掉当前资源
|
||||
// Filter out current resources
|
||||
const otherResource = currentResourceRef?.current
|
||||
? workflowResource.filter(
|
||||
r => r.res_id !== currentResourceRef?.current?.res_id,
|
||||
|
||||
@@ -24,8 +24,8 @@ import {
|
||||
} from '@coze-project-ide/framework';
|
||||
|
||||
/**
|
||||
* 注入到 workflow 内 project api 的能力。
|
||||
* 注:非响应式
|
||||
* The ability to inject project APIs into workflow.
|
||||
* Note: non-responsive
|
||||
*/
|
||||
export const useProjectApi = () => {
|
||||
const { sendOpen } = useSendMessageEvent();
|
||||
|
||||
@@ -233,7 +233,7 @@ export const useWorkflowResource = (): UseWorkflowResourceReturn => {
|
||||
subType: WorkflowMode.Workflow,
|
||||
tooltip: <WorkflowTooltip flowMode={WorkflowMode.Workflow} />,
|
||||
},
|
||||
// 开源版本暂不支持对话流
|
||||
// The open-source version does not currently support conversation streaming
|
||||
IS_OPEN_SOURCE
|
||||
? null
|
||||
: {
|
||||
|
||||
@@ -23,7 +23,7 @@ export {
|
||||
|
||||
export { WorkflowWidgetRegistry } from './registry';
|
||||
/**
|
||||
* 会话管理 registry
|
||||
* Session management registry
|
||||
*/
|
||||
export { ConversationRegistry } from './conversation/registry';
|
||||
export { useWorkflowResource } from './hooks/use-workflow-resource';
|
||||
|
||||
@@ -34,7 +34,7 @@ import { createDefaultPreset } from '../create-default-preset';
|
||||
export interface IDEClientProps {
|
||||
options: (ctx: IDEClientContext) => IDEClientOptions;
|
||||
container?: interfaces.Container;
|
||||
containerModules?: interfaces.ContainerModule[]; // 注入的 IOC 包
|
||||
containerModules?: interfaces.ContainerModule[]; // Injected IOC packet
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -35,12 +35,12 @@ export class ClientDefaultContribution
|
||||
protected readonly openerService: OpenerService;
|
||||
|
||||
/**
|
||||
* IDE 初始化阶段
|
||||
* IDE initialization phase
|
||||
*/
|
||||
onInit() {}
|
||||
|
||||
/**
|
||||
* 注册 commands
|
||||
* Registration commands
|
||||
* @param registry
|
||||
*/
|
||||
registerCommands(registry: CommandRegistry) {}
|
||||
|
||||
@@ -47,10 +47,10 @@ export function createDefaultPreset<CTX extends IDEClientContext>(
|
||||
const opts = optionsProvider(ctx);
|
||||
const plugins: Plugin[] = [];
|
||||
/**
|
||||
* 注册内置插件
|
||||
* Register built-in plugins
|
||||
*/
|
||||
plugins.push(
|
||||
createResourcePlugin(opts.resource || {}), // 资源系统
|
||||
createResourcePlugin(opts.resource || {}), // resource system
|
||||
createViewPlugin(
|
||||
opts.view || {
|
||||
widgetFactories: [],
|
||||
@@ -59,21 +59,21 @@ export function createDefaultPreset<CTX extends IDEClientContext>(
|
||||
defaultWidgets: [],
|
||||
},
|
||||
},
|
||||
), // 布局系统
|
||||
// Breaking change:bw 额外从业务侧引入
|
||||
), // layout system
|
||||
// Breaking change: bw additionally introduced from the business side
|
||||
createNavigationPlugin(opts.navigation || {}),
|
||||
createCommandPlugin(opts.command || {}), // 指令注册
|
||||
createHistoryPlugin(opts.history || {}), // 历史注册
|
||||
createLifecyclePlugin(opts), // IDE 生命周期注册
|
||||
createLabelPlugin(opts.label || {}), // 标签注册
|
||||
createShortcutsPlugin(opts.shortcut || {}), // 快捷键
|
||||
createPreferencesPlugin(opts.preferences || {}), // 偏好设置
|
||||
createCommandPlugin(opts.command || {}), // Instruction registration
|
||||
createHistoryPlugin(opts.history || {}), // historical registration
|
||||
createLifecyclePlugin(opts), // IDE Lifecycle Registration
|
||||
createLabelPlugin(opts.label || {}), // label registration
|
||||
createShortcutsPlugin(opts.shortcut || {}), // shortcut
|
||||
createPreferencesPlugin(opts.preferences || {}), // preferences
|
||||
createStylesPlugin({}),
|
||||
createContextMenuPlugin(), // 右键菜单注册
|
||||
createEventPlugin(), // 全局事件注册
|
||||
createContextMenuPlugin(), // Right-click menu to register
|
||||
createEventPlugin(), // global event registration
|
||||
);
|
||||
/**
|
||||
* client 扩展
|
||||
* client extension
|
||||
*/
|
||||
plugins.push(
|
||||
createLifecyclePlugin({
|
||||
@@ -96,7 +96,7 @@ export function createDefaultPreset<CTX extends IDEClientContext>(
|
||||
}),
|
||||
);
|
||||
/**
|
||||
* 注册业务扩展的插件
|
||||
* Register plugins for business extensions
|
||||
*/
|
||||
if (opts.plugins) {
|
||||
plugins.push(...opts.plugins);
|
||||
|
||||
@@ -50,7 +50,7 @@ export class Application {
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始应用
|
||||
* Start application
|
||||
*/
|
||||
async start(): Promise<void> {
|
||||
const contribs = this.contributionProvider.getContributions();
|
||||
@@ -69,7 +69,7 @@ export class Application {
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束应用
|
||||
* end application
|
||||
*/
|
||||
|
||||
async dispose(): Promise<void> {
|
||||
|
||||
@@ -27,13 +27,13 @@ export const ContextMatcher = Symbol('ContextMatcher');
|
||||
|
||||
export interface ContextMatcher {
|
||||
/**
|
||||
* 判断 expression 是否命中上下文
|
||||
* Determines whether the expression hits the context
|
||||
*/
|
||||
match: (expression: string) => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局 context key 上下文管理
|
||||
* Global context key context management
|
||||
*/
|
||||
@injectable()
|
||||
export class ContextKeyService implements ContextMatcher {
|
||||
|
||||
@@ -19,31 +19,31 @@ import { type MaybePromise } from '@flowgram-adapter/common';
|
||||
|
||||
export const LifecycleContribution = Symbol('LifecycleContribution');
|
||||
/**
|
||||
* IDE 全局生命周期注册
|
||||
* IDE Global Lifecycle Registration
|
||||
*/
|
||||
export interface LifecycleContribution {
|
||||
/**
|
||||
* IDE 注册阶段
|
||||
* IDE registration phase
|
||||
*/
|
||||
onInit?(): void;
|
||||
/**
|
||||
* IDE loading 阶段, 一般用于加载全局配置,如 i18n 数据
|
||||
* IDE loading phase, generally used to load global configuration, such as i18n data
|
||||
*/
|
||||
onLoading?(): MaybePromise<void>;
|
||||
/**
|
||||
* IDE 布局初始化阶段,在 onLoading 之后执行
|
||||
* IDE layout initialization phase, executed after onLoading
|
||||
*/
|
||||
onLayoutInit?(): MaybePromise<void>;
|
||||
/**
|
||||
* IDE 开始执行, 可以加载业务逻辑
|
||||
* The IDE starts to execute and the business logic can be loaded
|
||||
*/
|
||||
onStart?(): MaybePromise<void>;
|
||||
/**
|
||||
* 在浏览器 `beforeunload` 之前执行,如果返回true,则会阻止
|
||||
* Execute before the browser'beforeunload ', if it returns true, it will be blocked
|
||||
*/
|
||||
onWillDispose?(): boolean | void;
|
||||
/**
|
||||
* IDE 销毁
|
||||
* IDE destruction
|
||||
*/
|
||||
onDispose?(): void;
|
||||
}
|
||||
|
||||
@@ -37,14 +37,14 @@ export interface OpenHandler {
|
||||
export const OpenerService = Symbol('OpenerService');
|
||||
export interface OpenerService {
|
||||
/**
|
||||
* 跳转定位
|
||||
* Jump Positioning
|
||||
* @param uri
|
||||
* @param options
|
||||
*/
|
||||
open: (uri: URI, options?: OpenerOptions) => Promise<object | undefined>;
|
||||
|
||||
/**
|
||||
* 某个请求触发
|
||||
* A request is triggered
|
||||
*/
|
||||
onURIOpen: Event<{ uri: URI; options?: OpenerOptions }>;
|
||||
}
|
||||
|
||||
@@ -21,17 +21,17 @@ import { LifecycleContribution } from './lifecycle-contribution';
|
||||
|
||||
export interface PluginContext {
|
||||
/**
|
||||
* 获取 IOC 容器
|
||||
* Get IOC container
|
||||
*/
|
||||
container: interfaces.Container;
|
||||
/**
|
||||
* 获取 IOC 容器的 单例模块
|
||||
* Get the singleton module for the IOC container
|
||||
* @param identifier
|
||||
*/
|
||||
get: <T>(identifier: interfaces.ServiceIdentifier<T>) => T;
|
||||
|
||||
/**
|
||||
* 获取 IOC 容器的 多例模块
|
||||
* Get the multi-instance module of the IOC container
|
||||
*/
|
||||
getAll: <T>(identifier: interfaces.ServiceIdentifier<T>) => T[];
|
||||
}
|
||||
@@ -47,27 +47,27 @@ export interface PluginBindConfig {
|
||||
|
||||
interface PluginLifeCycle<CTX extends PluginContext, OPTS> {
|
||||
/**
|
||||
* IDE 注册阶段
|
||||
* IDE registration phase
|
||||
*/
|
||||
onInit?: (ctx: CTX, opts: OPTS) => void;
|
||||
/**
|
||||
* IDE loading 阶段, 一般用于加载全局配置,如 i18n 数据
|
||||
* IDE loading phase, generally used to load global configuration, such as i18n data
|
||||
*/
|
||||
onLoading?: (ctx: CTX, opts: OPTS) => MaybePromise<void>;
|
||||
/**
|
||||
* IDE 布局初始化阶段,在 onLoading 之后执行
|
||||
* IDE layout initialization phase, executed after onLoading
|
||||
*/
|
||||
onLayoutInit?: (ctx: CTX, opts: OPTS) => MaybePromise<void>;
|
||||
/**
|
||||
* IDE 开始执行, 可以加载业务逻辑
|
||||
* The IDE starts to execute and the business logic can be loaded
|
||||
*/
|
||||
onStart?: (ctx: CTX, opts: OPTS) => MaybePromise<void>;
|
||||
/**
|
||||
* 在浏览器 `beforeunload` 之前执行,如果返回true,则会阻止
|
||||
* Execute before the browser'beforeunload ', if it returns true, it will be blocked
|
||||
*/
|
||||
onWillDispose?: (ctx: CTX, opts: OPTS) => boolean | void;
|
||||
/**
|
||||
* IDE 销毁
|
||||
* IDE destruction
|
||||
*/
|
||||
onDispose?: (ctx: CTX, opts: OPTS) => void;
|
||||
}
|
||||
@@ -75,12 +75,12 @@ interface PluginLifeCycle<CTX extends PluginContext, OPTS> {
|
||||
export interface PluginConfig<OPTS, CTX extends PluginContext = PluginContext>
|
||||
extends PluginLifeCycle<CTX, OPTS> {
|
||||
/**
|
||||
* 插件 IOC 注册, 等价于 containerModule
|
||||
* Plugin IOC registration, equivalent to containerModule
|
||||
* @param ctx
|
||||
*/
|
||||
onBind?: (bindConfig: PluginBindConfig, opts: OPTS) => void;
|
||||
/**
|
||||
* IOC 模块,用于更底层的插件扩展
|
||||
* IOC module for lower-level plug-in extensions
|
||||
*/
|
||||
containerModules?: interfaces.ContainerModule[];
|
||||
}
|
||||
@@ -114,7 +114,7 @@ export function loadPlugins(
|
||||
}
|
||||
if (plugin.containerModules && plugin.containerModules.length > 0) {
|
||||
for (const module of plugin.containerModules) {
|
||||
// 去重
|
||||
// deduplicate
|
||||
if (!res.includes(module)) {
|
||||
res.push(module);
|
||||
}
|
||||
@@ -177,7 +177,7 @@ export function definePluginCreator<
|
||||
return {
|
||||
pluginId,
|
||||
initPlugin: () => {
|
||||
// 防止 plugin 被上层业务多次 init
|
||||
// Prevent the plugin from being inited multiple times by the upper business
|
||||
if (isInit) {
|
||||
return;
|
||||
}
|
||||
@@ -224,17 +224,17 @@ export function definePluginCreator<
|
||||
/**
|
||||
* @example
|
||||
* createLifecyclePlugin({
|
||||
* // IOC 注册
|
||||
* //IOC Registrationgistration
|
||||
* onBind(bind) {
|
||||
* bind('xxx').toSelf().inSingletonScope()
|
||||
* },
|
||||
* // IDE 初始化
|
||||
* //IDE initializationtialization
|
||||
* onInit() {
|
||||
* },
|
||||
* // IDE 销毁
|
||||
* //IDE destructionstruction
|
||||
* onDispose() {
|
||||
* },
|
||||
* // IOC 模块
|
||||
* //IOC moduledule
|
||||
* containerModules: [new ContainerModule(() => {})]
|
||||
* })
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@ import { injectable, postConstruct } from 'inversify';
|
||||
export const StorageService = Symbol('StorageService');
|
||||
|
||||
/**
|
||||
* 存储数据到缓存
|
||||
* Store data to cache
|
||||
*/
|
||||
export interface StorageService {
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
import { URI as Uri } from 'vscode-uri';
|
||||
|
||||
import { prioritizeAllSync, prioritizeAll } from './prioritizeable';
|
||||
@@ -241,23 +241,23 @@ export class URI {
|
||||
match(uri: URI) {
|
||||
const path = `/${uri.authority}${uri.path.toString()}`;
|
||||
const params: any[] = [];
|
||||
const pattern = `/${this.authority}${this.path.toString()}`; // 以 / 开头
|
||||
const pattern = `/${this.authority}${this.path.toString()}`; // Start with/
|
||||
let regexpSource = pattern
|
||||
.replace(/\/*\*?$/, '') // 去掉末尾的 / 和 /*
|
||||
.replace(/[\\.*+^${}|()[\]]/g, '\\$&') // 转译一些特殊字符
|
||||
.replace(/\/*\*?$/, '') // remove the/and/* at the endnd/* at the end
|
||||
.replace(/[\\. *+ ^ ${} | () [\]]/g, '\\ $&') //Translate some special characterse some special characters
|
||||
.replace(/\/:([\w-]+)(\?)?/g, (_, paramName, optional) => {
|
||||
// 收集 url 上的参数 /:param/, /:param?/
|
||||
//Collect the parameters on the url/: param/,/: param?/RL/: param/,/: param?/
|
||||
params.push({
|
||||
paramName,
|
||||
// eslint-disable-next-line eqeqeq
|
||||
optional: optional != null,
|
||||
});
|
||||
// 是否是可选参数
|
||||
// Is it an optional parameter?n optional parameter?
|
||||
return optional ? '/?([^\\/]+)?' : '/([^\\/]+)';
|
||||
});
|
||||
if (pattern.endsWith('*')) {
|
||||
params.push({ paramName: '*' });
|
||||
// 也许路径只有 *
|
||||
//Maybe the path is only *e path is only *
|
||||
regexpSource += pattern === '/*' ? '(.*)$' : '(?:\\/(.+)|\\/*)$';
|
||||
} else {
|
||||
regexpSource += '\\/*$';
|
||||
@@ -273,16 +273,16 @@ export interface URIHandler {
|
||||
|
||||
export namespace URIHandler {
|
||||
/**
|
||||
* 上层注册的优先级最高
|
||||
* Upper level registration has the highest priority
|
||||
*/
|
||||
export const MAX_PRIORITY = 500;
|
||||
/**
|
||||
* 默认兜底
|
||||
* Default bottom line
|
||||
*/
|
||||
export const DEFAULT_PRIORITY = 0;
|
||||
|
||||
/**
|
||||
* 优先级排序
|
||||
* prioritization
|
||||
* @param uri
|
||||
* @param handlers
|
||||
*/
|
||||
@@ -292,7 +292,7 @@ export namespace URIHandler {
|
||||
): Promise<T> {
|
||||
const prioritized = await prioritizeAll<T>(handlers, async handler => {
|
||||
const priority = handler.canHandle(uri);
|
||||
// boolean 情况默认采用 500
|
||||
// In the boolean case, 500 is used by default.
|
||||
if (typeof priority === 'boolean') {
|
||||
return priority ? MAX_PRIORITY : DEFAULT_PRIORITY;
|
||||
}
|
||||
@@ -303,7 +303,7 @@ export namespace URIHandler {
|
||||
export function findSync<T extends URIHandler>(uri: URI, handlers: T[]): T {
|
||||
const prioritized = prioritizeAllSync<T>(handlers, handler => {
|
||||
const priority = handler.canHandle(uri);
|
||||
// boolean 情况默认采用 500
|
||||
// In the boolean case, 500 is used by default.
|
||||
if (typeof priority === 'boolean') {
|
||||
return priority ? MAX_PRIORITY : DEFAULT_PRIORITY;
|
||||
}
|
||||
|
||||
@@ -36,10 +36,10 @@ export interface EventRegsiter {
|
||||
|
||||
export interface EventService {
|
||||
/**
|
||||
* 监听全局的事件
|
||||
* @param name 触发的事件名
|
||||
* @param handle 触发事件后执行
|
||||
* @param priority 优先级
|
||||
* Monitor global events
|
||||
* @Param name The name of the event fired
|
||||
* @Param handles execution after triggering event
|
||||
* @param priority priority
|
||||
*/
|
||||
listenGlobalEvent: (
|
||||
name: EventName,
|
||||
@@ -52,7 +52,7 @@ export const EventContribution = Symbol('EventContribution');
|
||||
|
||||
export interface EventContribution {
|
||||
/**
|
||||
* 注册 event
|
||||
* Register for events
|
||||
*/
|
||||
registerEvent: (service: EventService) => void;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ export class EventRegistry implements EventService, LifecycleContribution {
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局监听事件
|
||||
* global listening event
|
||||
*/
|
||||
listenGlobalEvent(
|
||||
name: EventName,
|
||||
@@ -93,9 +93,9 @@ export class EventRegistry implements EventService, LifecycleContribution {
|
||||
const { handlers } = eventRegister;
|
||||
const item = { handle, priority };
|
||||
/**
|
||||
* handlers 排序:
|
||||
* 1. 后注册先执行 (符合冒泡规则)
|
||||
* 2. 按 priority 排序
|
||||
* Handlers sort:
|
||||
* 1. Execute first after registration (in line with bubbling rules)
|
||||
* 2. Sort by priority
|
||||
*/
|
||||
handlers.unshift(item);
|
||||
handlers.sort((a, b) => b.priority - a.priority);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** 公共函数 */
|
||||
/** public function */
|
||||
|
||||
export {
|
||||
Emitter,
|
||||
|
||||
@@ -41,7 +41,7 @@ export interface LabelHandler {
|
||||
getIcon?: (uri: URI) => string | undefined | React.ReactNode;
|
||||
|
||||
/**
|
||||
* 自定义渲染 label
|
||||
* Custom render label
|
||||
*/
|
||||
renderer?: (uri: URI, opt?: any) => React.ReactNode;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ interface LabelChangeEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供 全局的 label 数据获取
|
||||
* Provide, global label data acquisition
|
||||
*/
|
||||
@injectable()
|
||||
export class LabelManager implements LabelService, LifecycleContribution {
|
||||
@@ -56,7 +56,7 @@ export class LabelManager implements LabelService, LifecycleContribution {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 label 的 icon
|
||||
* Get label icon
|
||||
* @param element
|
||||
*/
|
||||
getIcon(element: URI): string | React.ReactNode {
|
||||
@@ -72,7 +72,7 @@ export class LabelManager implements LabelService, LifecycleContribution {
|
||||
}
|
||||
|
||||
/**
|
||||
* label 的自定义渲染
|
||||
* Custom rendering of labels
|
||||
*/
|
||||
renderer(element: URI, opts?: any): ReactNode {
|
||||
const handler = this.findContribution(element).find(i => i.renderer);
|
||||
@@ -83,7 +83,7 @@ export class LabelManager implements LabelService, LifecycleContribution {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 label 名字
|
||||
* Get label name
|
||||
* @param element
|
||||
*/
|
||||
getName(element: URI): string {
|
||||
@@ -99,7 +99,7 @@ export class LabelManager implements LabelService, LifecycleContribution {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 label 的详细描述
|
||||
* Get a detailed description of the label
|
||||
* @param element
|
||||
*/
|
||||
getDescription(element: URI): string {
|
||||
@@ -136,7 +136,7 @@ export class LabelManager implements LabelService, LifecycleContribution {
|
||||
}
|
||||
|
||||
/**
|
||||
* label 变化后触发
|
||||
* Triggered after label change
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error
|
||||
// @ts-ignore
|
||||
|
||||
@@ -21,33 +21,33 @@ import { type LabelChangeEvent } from './label-handler';
|
||||
|
||||
export const LabelService = Symbol('LabelService');
|
||||
/**
|
||||
* 提供 全局的 label 数据获取
|
||||
* Provide, global label data acquisition
|
||||
*/
|
||||
export interface LabelService {
|
||||
/**
|
||||
* label 变化后触发
|
||||
* Triggered after label change
|
||||
*/
|
||||
get onChange(): Event<LabelChangeEvent>;
|
||||
|
||||
/**
|
||||
* 获取 label 的 icon
|
||||
* Get label icon
|
||||
* @param element
|
||||
*/
|
||||
getIcon: (element: URI) => string | React.ReactNode;
|
||||
|
||||
/**
|
||||
* 获取 label 的自定义渲染
|
||||
* Get custom rendering of label
|
||||
*/
|
||||
renderer: (element: URI, opts?: any) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* 获取 label 名字
|
||||
* Get label name
|
||||
* @param element
|
||||
*/
|
||||
getName: (element: URI) => string;
|
||||
|
||||
/**
|
||||
* 获取 label 的描述
|
||||
* Get the description of the label
|
||||
* @param element
|
||||
*/
|
||||
getDescription: (element: URI) => string;
|
||||
|
||||
@@ -29,7 +29,7 @@ export interface URILabelProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染 Label 的 react 组件
|
||||
* React component for rendering Label
|
||||
* @param props
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
@@ -25,8 +25,8 @@ export interface HistoryState {
|
||||
uri?: string;
|
||||
|
||||
/**
|
||||
* 在栈中的索引
|
||||
* react-router 使用「idx」,使用「fIdx」防止串数据
|
||||
* Index on the stack
|
||||
* React-router uses "idx" and "fIdx" to prevent string data
|
||||
*/
|
||||
fIdx?: number;
|
||||
}
|
||||
@@ -71,8 +71,8 @@ export class BrowserHistory implements Disposable {
|
||||
|
||||
init() {
|
||||
/**
|
||||
* 初始化的时候 index 必然为 null
|
||||
* 如果不是,说明有别的框架或者在直接使用 history.pushState 或者 history.replaceState 污染
|
||||
* When initializing, the index must be null.
|
||||
* If not, it means that there is another framework or pollution directly using history.pushState or history.replaceState
|
||||
*/
|
||||
if (this.idx === null) {
|
||||
this.history.replaceState({ ...this.state, fIdx: -1, uri: '' }, '');
|
||||
@@ -95,8 +95,8 @@ export class BrowserHistory implements Disposable {
|
||||
this.history.pushState(state, '', url);
|
||||
} catch {
|
||||
/**
|
||||
* history 还是有可能因为 state 或者浏览器等原因挂掉的
|
||||
* 降级成直接跳转
|
||||
* History may still die due to state or browser reasons
|
||||
* Downgrade to direct jump
|
||||
*/
|
||||
this.window.location.assign(url);
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ import {
|
||||
import { type URI } from '../common';
|
||||
import { BrowserHistory, type HistoryState } from './browser-history';
|
||||
|
||||
/** location 结构 */
|
||||
/** Location structure */
|
||||
interface Location {
|
||||
/** 唯一标识,一般来说是 uri */
|
||||
/** The unique identifier is usually a uri. */
|
||||
uri: URI;
|
||||
/** 预留字段 */
|
||||
/** reserved field */
|
||||
}
|
||||
|
||||
@injectable()
|
||||
@@ -68,7 +68,7 @@ class NavigationHistory {
|
||||
}
|
||||
|
||||
pushOrReplace(location: Location, replace = false) {
|
||||
// 如果处于回退状态,则清除之后所有的历史
|
||||
// If it is in a fallback state, all subsequent history is cleared
|
||||
if (this.stack.length > this.idx + 1) {
|
||||
this.stack = this.stack.slice(0, this.idx + 1);
|
||||
}
|
||||
@@ -99,7 +99,7 @@ class NavigationHistory {
|
||||
private go(delta: number) {
|
||||
const next = this.idx + delta;
|
||||
const nextLocation = this.stack[next];
|
||||
// 越界按照无效处理
|
||||
// Crossing the border is treated as invalid
|
||||
if (next >= this.stack.length || next < 0 || !nextLocation) {
|
||||
return;
|
||||
}
|
||||
@@ -126,12 +126,12 @@ class NavigationHistory {
|
||||
}
|
||||
|
||||
private listener = (state: HistoryState) => {
|
||||
/** 无法正确识别 state 时不做任何处理 */
|
||||
/** Do nothing when the state cannot be correctly recognized */
|
||||
if (!state || !isNumber(state.fIdx) || !state.uri) {
|
||||
return;
|
||||
}
|
||||
const { fIdx: idx } = state;
|
||||
/** 索引越界 */
|
||||
/** Index out of bounds */
|
||||
if (idx >= this.stack.length || idx < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class PreferencesManager {
|
||||
|
||||
public init(data: any) {
|
||||
/**
|
||||
* 从远程或者本地读取用户配置
|
||||
* Read user configuration remotely or locally
|
||||
*/
|
||||
Object.assign(this.preferences, data);
|
||||
this.preferencesChange.fire();
|
||||
@@ -41,13 +41,13 @@ class PreferencesManager {
|
||||
|
||||
public setSchema(schema: PreferenceSchema) {
|
||||
const { properties } = schema;
|
||||
/** 这里先做简单校验,后面要做整个 validateSchema */
|
||||
/** Here is a simple verification first, followed by the entire validateSchema. */
|
||||
if (!properties || !isObject(properties)) {
|
||||
return;
|
||||
}
|
||||
Object.entries<SchemaDecoration>(properties).forEach(([key, value]) => {
|
||||
if (this.schema.properties[key]) {
|
||||
// 重复定义的不覆盖,先报个警告
|
||||
// Repeatedly defined do not cover, report a warning first
|
||||
console.error(
|
||||
'Preference name collision detected in the schema for property: ',
|
||||
key,
|
||||
|
||||
@@ -29,9 +29,9 @@ import { Application, IDEContainerModule } from '../application';
|
||||
import { IDEContainerContext } from './context';
|
||||
|
||||
export interface IDEProviderProps {
|
||||
containerModules?: interfaces.ContainerModule[]; // 注入的 IOC 包
|
||||
containerModules?: interfaces.ContainerModule[]; // Injected IOC packet
|
||||
container?: interfaces.Container;
|
||||
customPluginContext?: (container: interfaces.Container) => PluginContext; // 自定义插件的上下文
|
||||
customPluginContext?: (container: interfaces.Container) => PluginContext; // Customize the plugin's context
|
||||
plugins?: PluginsProvider<any>;
|
||||
children?: React.ReactElement<any, any> | null;
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export interface IDEProviderRef {
|
||||
}
|
||||
|
||||
/**
|
||||
* IDE 容器
|
||||
* IDE Container
|
||||
*/
|
||||
const IDEProviderWithRef: ForwardRefRenderFunction<
|
||||
IDEProviderRef,
|
||||
@@ -55,7 +55,7 @@ const IDEProviderWithRef: ForwardRefRenderFunction<
|
||||
} = props;
|
||||
|
||||
/**
|
||||
* 创建 IOC 包
|
||||
* Create IOC package
|
||||
*/
|
||||
const container = useMemo(() => {
|
||||
const mainContainer: interfaces.Container =
|
||||
@@ -74,7 +74,7 @@ const IDEProviderWithRef: ForwardRefRenderFunction<
|
||||
}
|
||||
mainContainer.get(Application).init();
|
||||
return mainContainer;
|
||||
// @action 这里 props 数据如果更改不会触发刷新,不允许修改
|
||||
// @action Here props data will not trigger a refresh if changed, no modification is allowed
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -21,7 +21,7 @@ import { type interfaces } from 'inversify';
|
||||
import { IDEContainerContext } from './context';
|
||||
|
||||
/**
|
||||
* 获取 ide inversify container
|
||||
* Acquire ide inversified container
|
||||
*/
|
||||
export function useIDEContainer(): interfaces.Container {
|
||||
return React.useContext(IDEContainerContext);
|
||||
|
||||
@@ -19,7 +19,7 @@ import { type interfaces } from 'inversify';
|
||||
import { useIDEContainer } from './use-ide-container';
|
||||
|
||||
/**
|
||||
* 获取IDE的 IOC 模块
|
||||
* Get the IOC module of the IDE
|
||||
* @param identifier
|
||||
*/
|
||||
export function useIDEService<T>(identifier: interfaces.ServiceIdentifier): T {
|
||||
|
||||
@@ -21,7 +21,7 @@ import { type URI } from '../common';
|
||||
import { useIDEService } from './use-ide-service';
|
||||
|
||||
const useNavigation = (): {
|
||||
/** 可传入 URI 或 string,传入 string 时以 / 开头,和 react-router-dom 对齐 */
|
||||
/** You can pass in a URI or string, starting with/when passing in a string, aligned with react-router-dom */
|
||||
navigate: (uri: URI | string, replace?: boolean, options?: any) => void;
|
||||
history: NavigationHistory;
|
||||
back: () => Promise<void>;
|
||||
|
||||
@@ -34,7 +34,7 @@ export interface AutoSaveResourceOptions {
|
||||
uri: URI;
|
||||
}
|
||||
/**
|
||||
* 资源文件自动保存服务,目前只适用于文本文件
|
||||
* Resource file auto-save service, currently only available for text files
|
||||
*/
|
||||
@injectable()
|
||||
export abstract class AutoSaveResource<
|
||||
@@ -155,7 +155,7 @@ export abstract class AutoSaveResource<
|
||||
return;
|
||||
}
|
||||
await this.readContent(false);
|
||||
// TODO sync 逻辑需要刷新 widget 数据
|
||||
// TODO sync logic needs to refresh widget data
|
||||
// if (token.isCancellationRequested || this._dirty) {
|
||||
// return;
|
||||
// }
|
||||
@@ -163,7 +163,7 @@ export abstract class AutoSaveResource<
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动保存
|
||||
* auto save
|
||||
*/
|
||||
protected doAutoSave(): void {
|
||||
if (this.autoSave === 'on') {
|
||||
@@ -323,7 +323,7 @@ export abstract class AutoSaveResource<
|
||||
}
|
||||
|
||||
saveContent(content: CHANGE_SET, patch = false): void {
|
||||
// 若支持增量改动 (仅一层)
|
||||
// If incremental changes are supported (only one layer)
|
||||
let newContent = content;
|
||||
const preSaveContent =
|
||||
this.contentChanges[this.contentChanges.length - 1] ?? this.lastContent;
|
||||
@@ -344,7 +344,7 @@ export abstract class AutoSaveResource<
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取正在保存中的内容
|
||||
* Get the content being saved
|
||||
*/
|
||||
getPreSaveContent(): CHANGE_SET | undefined {
|
||||
return (
|
||||
|
||||
@@ -23,8 +23,8 @@ import {
|
||||
import { type URI, type URIHandler } from '../common';
|
||||
|
||||
export interface ResourceInfo {
|
||||
displayName?: string; // 显示标题
|
||||
lastModification?: number | string; // 最后修改时间
|
||||
displayName?: string; // Show title
|
||||
lastModification?: number | string; // Last modification time
|
||||
version?: number | string;
|
||||
}
|
||||
export interface Resource<T = any, INFO extends ResourceInfo = ResourceInfo>
|
||||
@@ -62,7 +62,7 @@ export const ResourceHandler = Symbol('ResourceHandler');
|
||||
export interface ResourceHandler<T extends Resource = Resource>
|
||||
extends URIHandler {
|
||||
/**
|
||||
* 创建资源
|
||||
* Create a resource
|
||||
* @param uri
|
||||
*/
|
||||
resolve: (uri: URI) => T;
|
||||
|
||||
@@ -18,33 +18,33 @@ import { getKeyLabel, isKeyStringMatch } from '../utils';
|
||||
|
||||
export interface Keybinding {
|
||||
/**
|
||||
* 关联 command,该 keybinding 触发后执行的 command
|
||||
* Associated command, the command executed after the keybinding is triggered
|
||||
*/
|
||||
command: string;
|
||||
/**
|
||||
* 关联的快捷键,like:meta c
|
||||
* Associated shortcuts, like: meta c
|
||||
*/
|
||||
keybinding: string;
|
||||
/**
|
||||
* 是否阻止浏览器的默认行为
|
||||
* Whether to block the browser's default behavior
|
||||
*/
|
||||
preventDefault?: boolean;
|
||||
/**
|
||||
* keybinding 触发上下文,和 contextkey service 关联
|
||||
* Keybinding triggering context, associated with the contextkey service
|
||||
*/
|
||||
when?: string;
|
||||
/**
|
||||
* 通过 keybinding 的触发 command 的参数
|
||||
* Parameters to trigger commands via keybinding
|
||||
*/
|
||||
args?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* kiybinding 相关导出方法
|
||||
* KiyBinding related export method
|
||||
*/
|
||||
export namespace Keybinding {
|
||||
/**
|
||||
* 匹配键盘事件是否 macth 快捷键配置
|
||||
* Match keyboard event whether macth shortcut configuration
|
||||
*/
|
||||
export const isKeyEventMatch = isKeyStringMatch;
|
||||
|
||||
|
||||
@@ -34,27 +34,27 @@ import { type Keybinding, KeybindingRegistry } from './keybinding';
|
||||
export interface ShortcutsHandler
|
||||
extends Partial<Pick<CommandHandler, 'execute' | 'isEnabled'>> {
|
||||
/**
|
||||
* 注册快捷键 Id
|
||||
* Register shortcut IDs
|
||||
*/
|
||||
commandId: string;
|
||||
/**
|
||||
* 注册快捷键 label
|
||||
* Register shortcut label
|
||||
*/
|
||||
commandLabel?: string;
|
||||
/**
|
||||
* 执行上下文
|
||||
* execution context
|
||||
*/
|
||||
when?: string;
|
||||
/**
|
||||
* 涉及到的快捷键
|
||||
* The shortcuts involved
|
||||
*/
|
||||
keybinding: string[] | string;
|
||||
/**
|
||||
* 是否阻止浏览器的默认行为
|
||||
* Whether to block the browser's default behavior
|
||||
*/
|
||||
preventDefault?: boolean;
|
||||
/**
|
||||
* 快捷键来源
|
||||
* Shortcut source
|
||||
*/
|
||||
source?: string;
|
||||
}
|
||||
@@ -67,7 +67,7 @@ export interface ShortcutsContribution {
|
||||
|
||||
export interface ShortcutsRegistry {
|
||||
/**
|
||||
* 新增 shortcutHandlers
|
||||
* Add shortcutHandlers
|
||||
*/
|
||||
registerHandlers: (...handlers: ShortcutsHandler[]) => void;
|
||||
}
|
||||
@@ -108,14 +108,14 @@ export class ShortcutsService
|
||||
}
|
||||
|
||||
/**
|
||||
* IDE 初始化阶段注册 listener
|
||||
* IDE initialization phase registration listener
|
||||
*/
|
||||
onInit(): void {
|
||||
document.addEventListener('keydown', this.listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* IDE 销毁阶段移除 listener
|
||||
* IDE destruction phase to remove listeners
|
||||
*/
|
||||
onDispose(): void {
|
||||
document.removeEventListener('keydown', this.listener);
|
||||
@@ -135,7 +135,7 @@ export class ShortcutsService
|
||||
logger.log('ShortcutsRegistry - listener', ev, keybindings);
|
||||
keybindings.forEach(keybinding => {
|
||||
/**
|
||||
* 定义的全局快捷键不生效于输入组件,避免冲突
|
||||
* Defined global shortcuts do not take effect on input components to avoid conflicts
|
||||
*/
|
||||
if (domEditable(ev.target as HTMLElement) && !keybinding.when) {
|
||||
return;
|
||||
@@ -146,7 +146,7 @@ export class ShortcutsService
|
||||
};
|
||||
|
||||
registerHandlers(...handlers: ShortcutsHandler[]): void {
|
||||
// 注册 handler
|
||||
// Registration handler
|
||||
handlers.forEach(handler => {
|
||||
const keybindings = Array.isArray(handler.keybinding)
|
||||
? handler.keybinding
|
||||
@@ -273,14 +273,14 @@ export class ShortcutsService
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 source 获取 ShortcutsHandler
|
||||
* Get ShortcutsHandler from source
|
||||
*/
|
||||
public getShortcutsBySource(source: string): ShortcutsHandler[] {
|
||||
return this.shortcutsHandlers.filter(v => v.source === source);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据键盘事件获取 keybinding
|
||||
* Get keybinding based on keyboard events
|
||||
*/
|
||||
public getKeybindingMatchKeyEvent(
|
||||
keybindings: Keybinding[],
|
||||
@@ -292,7 +292,7 @@ export class ShortcutsService
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 keybinding
|
||||
* Execute keybinding
|
||||
*/
|
||||
public executeKeybinding(keybinding: Keybinding, ev: KeyboardEvent) {
|
||||
this.commandRegistry.executeCommand(keybinding.command, keybinding.args);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { isAppleDevice } from './device';
|
||||
|
||||
// 键盘事件 keyCode 别名
|
||||
// Keyboard Event keyCode Alias
|
||||
const aliasKeyCodeMap: Record<string, number | number[]> = {
|
||||
'0': 48,
|
||||
'1': 49,
|
||||
@@ -135,7 +135,7 @@ const modifierKey: any = {
|
||||
},
|
||||
};
|
||||
|
||||
// 根据 event 计算激活键数量
|
||||
// Number of activation keys based on event
|
||||
function countKeyByEvent(event: KeyboardEvent): number {
|
||||
const countOfModifier = Object.keys(modifierKey).reduce((total, key) => {
|
||||
if (modifierKey[key](event)) {
|
||||
@@ -145,7 +145,7 @@ function countKeyByEvent(event: KeyboardEvent): number {
|
||||
return total;
|
||||
}, 0);
|
||||
|
||||
// 16 17 18 91 92 是修饰键的 keyCode,如果 keyCode 是修饰键,那么激活数量就是修饰键的数量,如果不是,那么就需要 +1
|
||||
// 16 17 18 91 92 is the keyCode of the modifier key. If keyCode is a modifier key, then the number of activations is the number of modifier keys. If not, then + 1 is required.
|
||||
return [16, 17, 18, 91, 92].includes(event.keyCode)
|
||||
? countOfModifier
|
||||
: countOfModifier + 1;
|
||||
@@ -162,11 +162,11 @@ export function isKeyStringMatch(
|
||||
keyString: string,
|
||||
exactMatch = true,
|
||||
): boolean {
|
||||
// 浏览器自动补全 input 的时候,会触发 keyDown、keyUp 事件,但此时 event.key 等为空
|
||||
// When the browser automatically completes the input, keyDown and keyUp events will be triggered, but at this time event.key, etc. are empty
|
||||
if (!event.key || !keyString) {
|
||||
return false;
|
||||
}
|
||||
// 字符串依次判断是否有组合键
|
||||
// String in turn determines whether there is a key combination
|
||||
const genArr = keyString.split(/\s+/);
|
||||
let genLen = 0;
|
||||
|
||||
@@ -177,9 +177,9 @@ export function isKeyStringMatch(
|
||||
// }
|
||||
|
||||
for (const key of genArr) {
|
||||
// 组合键
|
||||
// key combination
|
||||
const genModifier = modifierKey[key];
|
||||
// keyCode 别名
|
||||
// keyCode alias
|
||||
const aliasKeyCode: number | number[] = aliasKeyCodeMap[key.toLowerCase()];
|
||||
|
||||
if (
|
||||
@@ -191,10 +191,10 @@ export function isKeyStringMatch(
|
||||
}
|
||||
|
||||
/**
|
||||
* 需要判断触发的键位和监听的键位完全一致,判断方法就是触发的键位里有且等于监听的键位
|
||||
* genLen === genArr.length 能判断出来触发的键位里有监听的键位
|
||||
* countKeyByEvent(event) === genArr.length 判断出来触发的键位数量里有且等于监听的键位数量
|
||||
* 主要用来防止按组合键其子集也会触发的情况,例如监听 ctrl+a 会触发监听 ctrl 和 a 两个键的事件。
|
||||
* It is necessary to determine that the triggered key is exactly the same as the monitored key. The judgment method is that the triggered key is equal to the monitored key.
|
||||
* genLen == genArr.length can determine that there is a listening key among the triggered keys
|
||||
* countKeyByEvent (event) === genArr.length The number of keys triggered by the judgment is equal to the number of keys listened to
|
||||
* It is mainly used to prevent the situation that pressing a subset of the key combination will also trigger, such as listening to the event that ctrl + a will trigger listening to the two keys ctrl and a.
|
||||
*/
|
||||
if (exactMatch) {
|
||||
return genLen === genArr.length && countKeyByEvent(event) === genArr.length;
|
||||
|
||||
@@ -36,13 +36,13 @@ class ColorService {
|
||||
@named(ColorContribution)
|
||||
colorContributions: ContributionProvider<ColorContribution>;
|
||||
|
||||
/** 颜色表 */
|
||||
/** Color chart */
|
||||
private colors: Record<string, ColorDefinition> = {};
|
||||
|
||||
/** 颜色的 schema */
|
||||
/** Color schema */
|
||||
private schema: ColorSchemaType = { type: 'object', properties: {} };
|
||||
|
||||
/** 颜色偏好设置的 schema */
|
||||
/** Color preference schema */
|
||||
// private referenceSchema = { type: 'string', enum: [], enumDescriptions: [] };
|
||||
|
||||
init() {
|
||||
@@ -52,7 +52,7 @@ class ColorService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量注册 color
|
||||
* Bulk registration color
|
||||
*/
|
||||
register(...colors: ColorDefinition[]): Disposable[] {
|
||||
return colors.map(definition => {
|
||||
@@ -67,7 +67,7 @@ class ColorService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销 color
|
||||
* Logout color
|
||||
*/
|
||||
deregisterColor(id: string): void {
|
||||
delete this.colors[id];
|
||||
@@ -75,7 +75,7 @@ class ColorService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的 color definition
|
||||
* Get all color definitions
|
||||
*/
|
||||
getColors(): ColorDefinition[] {
|
||||
return Object.keys(this.colors).map(id => this.colors[id]);
|
||||
@@ -86,12 +86,12 @@ class ColorService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 theme 的 color value
|
||||
* Gets the color value of the specified theme
|
||||
*/
|
||||
getThemeColor(id: string, themeType: ThemeType) {
|
||||
const color = this.getColor(id);
|
||||
let value = color.defaults?.[themeType];
|
||||
// 色值可以从其他色值继承
|
||||
// Color values can be inherited from other color values
|
||||
if (
|
||||
value &&
|
||||
typeof value === 'string' &&
|
||||
@@ -105,14 +105,14 @@ class ColorService {
|
||||
}
|
||||
|
||||
toCssColor(id: string, themeType: ThemeType) {
|
||||
// 转换为变量名
|
||||
// Convert to variable name
|
||||
const variableName = `--${id.replace(/\./g, '-')}`;
|
||||
const value = this.getThemeColor(id, themeType);
|
||||
return `${variableName}: ${value};`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有色值转化为 css 变量
|
||||
* All color values are converted to CSS variables
|
||||
*/
|
||||
toCss(themeType: ThemeType) {
|
||||
return `body {\n${this.getColors()
|
||||
|
||||
@@ -45,14 +45,14 @@ class StylingService {
|
||||
static readonly PREFIX = 'flowide';
|
||||
|
||||
/**
|
||||
* 后面移到 map 里面,现在暂时没想好
|
||||
* Move it to the map later, I haven't thought about it for the time being.
|
||||
*/
|
||||
private cssElement: HTMLStyleElement | undefined;
|
||||
|
||||
private css = new Map<string, HTMLStyleElement>();
|
||||
|
||||
/**
|
||||
* 收集所有 css 挂载到 <head> 上
|
||||
* Collect all css mounts to < head >
|
||||
*/
|
||||
apply(theme: Theme) {
|
||||
const rules: string[] = [];
|
||||
|
||||
@@ -41,13 +41,13 @@ class ThemeService {
|
||||
@inject(PreferencesManager)
|
||||
protected readonly preferencesManager: PreferencesManager;
|
||||
|
||||
/** 所有注册的 theme */
|
||||
/** All registered themes */
|
||||
private themes: Map<string, Theme> = new Map([
|
||||
[DEFAULT_THEME.type, DEFAULT_THEME],
|
||||
[DEFAULT_LIGHT_THEME.type, DEFAULT_LIGHT_THEME],
|
||||
]);
|
||||
|
||||
/** 当前 theme */
|
||||
/** Current theme */
|
||||
private current: Theme = DEFAULT_THEME;
|
||||
|
||||
private readonly themeChange = new Emitter<ThemeDidChangeEvent>();
|
||||
@@ -57,7 +57,7 @@ class ThemeService {
|
||||
|
||||
init() {
|
||||
this.changeWithPreferences();
|
||||
// 先手动触发一次 change 模拟从 preference 读取配置
|
||||
// First manually trigger a change simulation to read the configuration from preference
|
||||
this.preferencesManager.onDidPreferencesChange(() => {
|
||||
this.changeWithPreferences();
|
||||
});
|
||||
@@ -76,7 +76,7 @@ class ThemeService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 theme
|
||||
* Register theme
|
||||
*/
|
||||
register(...themes: Theme[]) {
|
||||
themes.forEach(theme => this.themes.set(theme.id, theme));
|
||||
|
||||
@@ -30,7 +30,7 @@ export default defineConfig({
|
||||
exclude: [
|
||||
'**/node_modules/**',
|
||||
'**/dist/**',
|
||||
'**/lib/**', // lib 编译结果忽略掉
|
||||
'**/lib/**', // The lib compilation result is ignored
|
||||
'**/cypress/**',
|
||||
'**/.{idea,git,cache,output,temp}/**',
|
||||
'**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build}.config.*',
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -57,7 +57,7 @@ const useFocusResource = ({
|
||||
itemHeight: config?.itemHeight || ITEM_HEIGHT,
|
||||
});
|
||||
|
||||
// 如果在视图内, 则不滚
|
||||
// If in view, do not roll
|
||||
if (
|
||||
scrollTop > scrollWrapper.current.scrollTop &&
|
||||
scrollTop <
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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[],
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 _'
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** 透传 sdk */
|
||||
/** pass-through sdk */
|
||||
export {
|
||||
IDEClient,
|
||||
ReactWidget,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
// 下边的 opacity、width 设置原因:
|
||||
// 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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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>;
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 资源类型
|
||||
* resource type
|
||||
*/
|
||||
export type ResourceType = 'workflow' | 'knowledge' | 'ui-builder';
|
||||
/**
|
||||
* 模式类型
|
||||
* schema type
|
||||
*/
|
||||
export type ModeType = 'dev' | 'ui-builder';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user