feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
bindContributionProvider,
|
||||
bindContributions,
|
||||
type AsClass,
|
||||
} from '@flowgram-adapter/common';
|
||||
|
||||
import { definePluginCreator, LifecycleContribution } from '../common';
|
||||
import { LabelService } from './label-service';
|
||||
import { LabelManager } from './label-manager';
|
||||
import { LabelHandler } from './label-handler';
|
||||
|
||||
export interface LabelPluginOptions {
|
||||
handlers?: (AsClass<LabelHandler> | LabelHandler)[];
|
||||
}
|
||||
|
||||
export const createLabelPlugin = definePluginCreator<LabelPluginOptions>({
|
||||
onBind: ({ bind }, opts) => {
|
||||
bindContributions(bind, LabelManager, [LifecycleContribution]);
|
||||
bind(LabelService).toService(LabelManager);
|
||||
bindContributionProvider(bind, LabelHandler);
|
||||
if (opts.handlers) {
|
||||
opts.handlers.forEach(handler => {
|
||||
if (typeof handler === 'function') {
|
||||
bind(handler).toSelf().inSingletonScope();
|
||||
bind(LabelHandler).toService(handler);
|
||||
} else {
|
||||
bind(LabelHandler).toConstantValue(handler);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
23
frontend/packages/project-ide/core/src/label/index.ts
Normal file
23
frontend/packages/project-ide/core/src/label/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { type LabelChangeEvent, LabelHandler } from './label-handler';
|
||||
export { LabelService } from './label-service';
|
||||
export {
|
||||
createLabelPlugin,
|
||||
type LabelPluginOptions,
|
||||
} from './create-label-plugin';
|
||||
export { URILabel } from './uri-label';
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type Event } from '@flowgram-adapter/common';
|
||||
|
||||
import { type URI } from '../common';
|
||||
export interface LabelChangeEvent {
|
||||
affects: (element: object) => boolean;
|
||||
}
|
||||
|
||||
export const LabelHandler = Symbol('LabelHandler');
|
||||
|
||||
export interface LabelHandler {
|
||||
/**
|
||||
* Emit when something has changed that may result in this label provider returning a different
|
||||
* value for one or more properties (name, icon etc).
|
||||
*/
|
||||
readonly onChange?: Event<LabelChangeEvent>;
|
||||
/**
|
||||
* whether this contribution can handle the given element and with what priority.
|
||||
* All contributions are ordered by the returned number if greater than zero. The highest number wins.
|
||||
* If two or more contributions return the same positive number one of those will be used. It is undefined which one.
|
||||
*/
|
||||
canHandle: (uri: URI) => number;
|
||||
/**
|
||||
* returns an icon class for the given element.
|
||||
*/
|
||||
getIcon?: (uri: URI) => string | undefined | React.ReactNode;
|
||||
|
||||
/**
|
||||
* 自定义渲染 label
|
||||
*/
|
||||
renderer?: (uri: URI, opt?: any) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* returns a short name for the given element.
|
||||
*/
|
||||
getName?: (uri: URI) => string | undefined;
|
||||
|
||||
/**
|
||||
* returns a long name for the given element.
|
||||
*/
|
||||
getDescription?: (uri: URI) => string | undefined;
|
||||
|
||||
/**
|
||||
* Check whether the given element is affected by the given change event.
|
||||
* Contributions delegating to the label provider can use this hook
|
||||
* to perform a recursive check.
|
||||
*/
|
||||
affects?: (uri: URI, event: LabelChangeEvent) => boolean;
|
||||
}
|
||||
146
frontend/packages/project-ide/core/src/label/label-manager.ts
Normal file
146
frontend/packages/project-ide/core/src/label/label-manager.ts
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
import { injectable, inject, named } from 'inversify';
|
||||
import { Emitter, type Event, ContributionProvider } from '@flowgram-adapter/common';
|
||||
|
||||
import {
|
||||
type URI,
|
||||
prioritizeAllSync,
|
||||
type LifecycleContribution,
|
||||
} from '../common';
|
||||
import { type LabelService } from './label-service';
|
||||
import { LabelHandler } from './label-handler';
|
||||
|
||||
interface LabelChangeEvent {
|
||||
affects: (element: URI) => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供 全局的 label 数据获取
|
||||
*/
|
||||
@injectable()
|
||||
export class LabelManager implements LabelService, LifecycleContribution {
|
||||
protected readonly onChangeEmitter = new Emitter<LabelChangeEvent>();
|
||||
|
||||
@inject(ContributionProvider)
|
||||
@named(LabelHandler)
|
||||
protected readonly contributionProvider: ContributionProvider<LabelHandler>;
|
||||
|
||||
onInit() {
|
||||
const contributions = this.contributionProvider.getContributions();
|
||||
for (const contribution of contributions) {
|
||||
if (contribution.onChange) {
|
||||
contribution.onChange(event => {
|
||||
this.onChangeEmitter.fire({
|
||||
affects: element => this.affects(element, event),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 label 的 icon
|
||||
* @param element
|
||||
*/
|
||||
getIcon(element: URI): string | React.ReactNode {
|
||||
const contributions = this.findContribution(element);
|
||||
for (const contribution of contributions) {
|
||||
const value = contribution.getIcon && contribution.getIcon(element);
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* label 的自定义渲染
|
||||
*/
|
||||
renderer(element: URI, opts?: any): ReactNode {
|
||||
const handler = this.findContribution(element).find(i => i.renderer);
|
||||
if (!handler || !handler.renderer) {
|
||||
return null;
|
||||
}
|
||||
return handler.renderer(element, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 label 名字
|
||||
* @param element
|
||||
*/
|
||||
getName(element: URI): string {
|
||||
const contributions = this.findContribution(element);
|
||||
for (const contribution of contributions) {
|
||||
const value = contribution.getName && contribution.getName(element);
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 label 的详细描述
|
||||
* @param element
|
||||
*/
|
||||
getDescription(element: URI): string {
|
||||
const contributions = this.findContribution(element);
|
||||
for (const contribution of contributions) {
|
||||
const value =
|
||||
contribution.getDescription && contribution.getDescription(element);
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
protected affects(element: URI, event: LabelChangeEvent): boolean {
|
||||
if (event.affects(element)) {
|
||||
return true;
|
||||
}
|
||||
for (const contribution of this.findContribution(element)) {
|
||||
if (contribution.affects && contribution.affects(element, event as any)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected findContribution(element: URI): LabelHandler[] {
|
||||
const prioritized = prioritizeAllSync(
|
||||
this.contributionProvider.getContributions(),
|
||||
contrib => contrib.canHandle(element),
|
||||
);
|
||||
return prioritized.map(c => c.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* label 变化后触发
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error
|
||||
// @ts-ignore
|
||||
get onChange(): Event<LabelChangeEvent> {
|
||||
return this.onChangeEmitter.event;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type Event } from '@flowgram-adapter/common';
|
||||
|
||||
import { type URI } from '../common';
|
||||
import { type LabelChangeEvent } from './label-handler';
|
||||
|
||||
export const LabelService = Symbol('LabelService');
|
||||
/**
|
||||
* 提供 全局的 label 数据获取
|
||||
*/
|
||||
export interface LabelService {
|
||||
/**
|
||||
* label 变化后触发
|
||||
*/
|
||||
get onChange(): Event<LabelChangeEvent>;
|
||||
|
||||
/**
|
||||
* 获取 label 的 icon
|
||||
* @param element
|
||||
*/
|
||||
getIcon: (element: URI) => string | React.ReactNode;
|
||||
|
||||
/**
|
||||
* 获取 label 的自定义渲染
|
||||
*/
|
||||
renderer: (element: URI, opts?: any) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* 获取 label 名字
|
||||
* @param element
|
||||
*/
|
||||
getName: (element: URI) => string;
|
||||
|
||||
/**
|
||||
* 获取 label 的描述
|
||||
* @param element
|
||||
*/
|
||||
getDescription: (element: URI) => string;
|
||||
}
|
||||
49
frontend/packages/project-ide/core/src/label/uri-label.tsx
Normal file
49
frontend/packages/project-ide/core/src/label/uri-label.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import { URI } from '../common';
|
||||
import {
|
||||
type LabelChangeEvent,
|
||||
LabelService,
|
||||
useIDEService,
|
||||
useRefresh,
|
||||
} from '../';
|
||||
|
||||
export interface URILabelProps {
|
||||
uri: string | URI;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染 Label 的 react 组件
|
||||
* @param props
|
||||
* @constructor
|
||||
*/
|
||||
export const URILabel: React.FC<URILabelProps> = props => {
|
||||
const uri = typeof props.uri === 'string' ? new URI(props.uri) : props.uri;
|
||||
const labelService = useIDEService<LabelService>(LabelService);
|
||||
const refresh = useRefresh();
|
||||
useEffect(() => {
|
||||
const dispose = labelService.onChange((event: LabelChangeEvent) => {
|
||||
if (event.affects(uri)) {
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
return () => dispose.dispose();
|
||||
}, []);
|
||||
return <>{labelService.renderer(uri)}</>;
|
||||
};
|
||||
Reference in New Issue
Block a user