feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -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);
}
});
}
},
});

View 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';

View File

@@ -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;
}

View 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;
}
}

View File

@@ -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;
}

View 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)}</>;
};