coze-studio/frontend/packages/project-ide/view/src/widget-manager.ts

181 lines
5.6 KiB
TypeScript

/*
* 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 { inject, injectable, type interfaces, named } from 'inversify';
import {
type AsClass,
ContributionProvider,
type MaybePromise,
} from '@flowgram-adapter/common';
import { ContainerFactory, type URI } from '@coze-project-ide/core';
import { WidgetFactory } from './widget/widget-factory';
import { CustomRenderWidgetFactory } from './widget/react-widgets/custom-render-widget-factory';
import { type ReactWidget } from './widget/react-widget';
import { type Widget } from './lumino/widgets';
// import { ViewRendererRegistry } from './view-render-registry';
export interface FlowWidget extends Widget {
init?: (opt?: any) => void;
}
@injectable()
export class WidgetManager {
protected readonly widgets = new Map<string, Widget>();
protected widgetFactories: WidgetFactory[] = [];
@inject(CustomRenderWidgetFactory)
protected customRenderWidgetFactory: CustomRenderWidgetFactory;
@inject(ContributionProvider)
@named(WidgetFactory)
protected readonly factoryProvider: ContributionProvider<WidgetFactory>;
@inject(ContainerFactory) containerFactory: ContainerFactory;
init(widgetFactories?: WidgetFactory[]) {
this.widgetFactories = widgetFactories || [];
for (const factory of this.factoryProvider.getContributions()) {
this.widgetFactories.push(factory);
}
}
getFactoryFromURI(uri: URI): WidgetFactory | undefined {
for (const factory of this.widgetFactories) {
if (factory.match && factory.match.test(uri.toString())) {
return factory;
}
if (factory.canHandle && factory.canHandle(uri)) {
return factory;
}
}
}
// @inject(ViewRendererRegistry) readonly rendererRegistry: ViewRendererRegistry;
protected readonly pendingWidgetPromises = new Map<
string,
MaybePromise<Widget>
>();
getWidget(id: string): Widget | undefined {
for (const [key, widget] of this.widgets.entries()) {
if (id === key) {
return widget;
}
}
return undefined;
}
getWidgetFromURI(uri: URI, factory?: WidgetFactory): Widget | undefined {
const id = this.uriToWidgetID(uri, factory);
return this.getWidget(id);
}
uriToWidgetID(uri: URI, factory?: WidgetFactory): string {
factory = factory || this.getFactoryFromURI(uri);
return factory?.getId?.(uri) || uri.withoutQuery().toString();
}
getAllWidgets(): Widget[] {
const result: Widget[] = [];
for (const [_, widget] of this.widgets.entries()) {
result.push(widget);
}
return result;
}
setWidget(id: string, widget: ReactWidget | Widget) {
this.widgets.set(id, widget);
widget.disposed.connect(() => this.widgets.delete(id));
}
protected doGetWidget<T extends Widget>(
id: string,
): MaybePromise<T> | undefined {
const pendingWidget =
this.widgets.get(id) ?? this.pendingWidgetPromises.get(id);
if (pendingWidget) {
return pendingWidget as MaybePromise<T>;
}
return undefined;
}
async createSubWidget<T extends ReactWidget>(
uri: URI,
widget: AsClass<ReactWidget>,
): Promise<T> {
const widgetId = this.uriToWidgetID(uri);
const currentWidget = this.getWidget(widgetId);
if (currentWidget) {
return currentWidget as T;
}
const childContainer = this.containerFactory.createChild();
childContainer.bind(widget).toSelf().inSingletonScope();
const createWidget = childContainer.get<ReactWidget>(widget);
createWidget.id = widgetId;
createWidget.init(uri, childContainer);
this.setWidget(widgetId, createWidget);
return createWidget as T;
}
/**
* 使用工厂模式注册
*/
async getOrCreateWidgetFromURI<T extends ReactWidget>(
uri: URI,
factory?: WidgetFactory,
): Promise<T> {
factory = factory || this.getFactoryFromURI(uri);
if (!factory) {
throw Error(`No widget factory '${uri.toString()}' has been registered.`);
}
const widgetId = this.uriToWidgetID(uri, factory);
const existingWidget = this.doGetWidget<T>(widgetId);
if (existingWidget) {
return existingWidget;
}
try {
let widgetPromise = factory.createWidget?.(uri);
let childContainer: interfaces.Container | undefined;
if (!widgetPromise && factory.widget) {
childContainer = this.containerFactory.createChild();
childContainer.bind(factory.widget).toSelf().inSingletonScope();
widgetPromise = childContainer.get<ReactWidget>(factory.widget);
}
if (!widgetPromise && factory.render) {
childContainer = this.containerFactory.createChild();
widgetPromise = this.customRenderWidgetFactory(childContainer);
}
if (!widgetPromise) {
throw Error('No widget createWidget');
}
this.pendingWidgetPromises.set(widgetId, widgetPromise);
const widget = await widgetPromise;
if (factory.render) {
widget.render = factory.render;
}
widget.id = widgetId;
widget.init(uri, childContainer);
this.setWidget(widgetId, widget);
return widget as T;
} finally {
this.pendingWidgetPromises.delete(widgetId);
}
}
}