improved height managing
This commit is contained in:
parent
e51d9a2fac
commit
5213e2bdfa
|
|
@ -33,11 +33,11 @@
|
|||
</header>
|
||||
|
||||
<!-- Wrapper per compensare header fisso -->
|
||||
<div class="pt-16 flex flex-col flex-1 h-full overflow-hidden">
|
||||
<div class="pt-16 flex flex-col flex-1 h-[calc(100vh-64px)] overflow-hidden">
|
||||
|
||||
<ul class="nav fixed w-full top-16 z-40
|
||||
flex gap-1 px-3
|
||||
bg-[#d8f4e7] border-b-none shadow-sm">
|
||||
bg-[#d8f4e7] border-b-none shadow-sm h-10">
|
||||
|
||||
<li class="nav-item">
|
||||
<a routerLink="/editor" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }" tabindex="-1" class="nav-link
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
|
||||
|
||||
<!-- Contenuto principale scrollabile -->
|
||||
<main class="main-content w-full h-full mt-12 overflow-hidden ml-2 bg-white pb-4 pr-4 rounded-md shadow-sm">
|
||||
<main class="main-content w-full h-[calc(100vh-104px)] mt-12 overflow-hidden ml-2 bg-white pb-4 pr-4 rounded-md shadow-sm">
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export type FlowVisibility = 'public' | 'private';
|
||||
|
||||
import { INodeDefinitionType } from "./node-types";
|
||||
|
||||
export type FlowVisibility = 'PUBLIC' | 'PRIVATE';
|
||||
|
||||
export type Flow = {
|
||||
id: string;
|
||||
|
|
@ -8,11 +8,30 @@ export type Flow = {
|
|||
visibility: FlowVisibility;
|
||||
data: FlowData;
|
||||
author: string;
|
||||
description?: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export class FlowData {
|
||||
|
||||
constructor(public nodes: any[] = [], public edges: any[] = []) {}
|
||||
};
|
||||
export type FlowData = {
|
||||
nodes: INodeModel[];
|
||||
connections: IConnectionModel[];
|
||||
}
|
||||
|
||||
export type INodeModel = {
|
||||
key: string;
|
||||
name?: string;
|
||||
position: { x: number, y: number } | null;
|
||||
parameters?: Record<string, any> | null;
|
||||
nodeDefinition: INodeDefinitionType;
|
||||
}
|
||||
|
||||
export type IConnectionModel = {
|
||||
key: string;
|
||||
sourceNode: string;
|
||||
sourceField: string;
|
||||
targetNode: string;
|
||||
targetField: string;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
|
||||
export interface INodeDefinitionType {
|
||||
name: string;
|
||||
category: string;
|
||||
outputs: Record<string, IOObject>;
|
||||
inputs: Record<string, IOObject>;
|
||||
fixedParameters?: Record<string, IParameter> | null;
|
||||
runtimeParameters?: Record<string, IParameter> | null;
|
||||
}
|
||||
|
||||
export interface IExecutionNodeType extends INodeDefinitionType {
|
||||
executor: string;
|
||||
outputMappers?: Record<string, IMapper>;
|
||||
inputMappers?: Record<string, IMapper>;
|
||||
}
|
||||
|
||||
export interface IInputNodeType extends INodeDefinitionType {
|
||||
simulabe: boolean;
|
||||
}
|
||||
|
||||
export interface IHumanInteractionNodeType extends INodeDefinitionType {
|
||||
simulabe: boolean;
|
||||
interactionDescription: string;
|
||||
}
|
||||
|
||||
export interface IIOutputNodeType extends INodeDefinitionType {
|
||||
}
|
||||
|
||||
export interface IMapper {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface ITraslatorMapper extends IMapper {
|
||||
translation: string;
|
||||
}
|
||||
|
||||
export interface IDirectMapper extends IMapper {
|
||||
fieldName: string;
|
||||
}
|
||||
|
||||
export interface IParserMapper extends IMapper {
|
||||
from?: string;
|
||||
to?: string;
|
||||
fieldName: string;
|
||||
multiSeparator?: string;
|
||||
}
|
||||
|
||||
export interface IParameter {
|
||||
name: string;
|
||||
label: string;
|
||||
description: string;
|
||||
type: ParameterType;
|
||||
required: boolean;
|
||||
validations?: IValidation[];
|
||||
specificAttributes?: any;
|
||||
};
|
||||
|
||||
export enum ParameterType {
|
||||
Text = 'Text',
|
||||
LongText = 'LongText',
|
||||
Select = 'Select',
|
||||
DynamicMultiOptions = 'DynamicMultiOptions',
|
||||
Number = 'Number',
|
||||
Boolean = 'Boolean'
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface IExecutor {
|
||||
identifier: string,
|
||||
name: string,
|
||||
description: string,
|
||||
inputs: Record<string, IOObject>,
|
||||
outputs: Record<string, IOObject>,
|
||||
mandatoryParameters: IParameter[],
|
||||
}
|
||||
|
||||
export enum IOType {
|
||||
TEXT = 'TEXT',
|
||||
//CSV = 'CSV',
|
||||
}
|
||||
|
||||
export interface IOObject {
|
||||
type: IOType;
|
||||
multiple: boolean;
|
||||
}
|
||||
|
||||
export interface IValidation {
|
||||
validator: string;
|
||||
value?: number | string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface IFlowError {
|
||||
type: string;
|
||||
node: string | undefined;
|
||||
connection: string | undefined;
|
||||
input: string | undefined;
|
||||
output: string | undefined;
|
||||
parameter: string | undefined;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { GetSchemes, ClassicPreset } from "rete";
|
||||
import { INodeModel } from "./flow";
|
||||
|
||||
export type HFNode = ClassicPreset.Node & {
|
||||
data?: {
|
||||
value: INodeModel;
|
||||
};
|
||||
};
|
||||
|
||||
export type HFConnection = ClassicPreset.Connection<HFNode, HFNode>;
|
||||
|
||||
export type HFSchemes = GetSchemes<HFNode, HFConnection>;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<div
|
||||
class="transition-all duration-300 ease-in-out overflow-hidden"
|
||||
class="transition-all duration-300 ease-in-out overflow-hidden h-[calc(100vh-104px)]"
|
||||
[class.w-14]="collapsed()"
|
||||
[class.w-72]="!collapsed()"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { Flow, FlowData } from "@models/flow";
|
||||
import { Flow, FlowData, FlowVisibility } from "@models/flow";
|
||||
import { FlowsCallServiceBase } from "./flows-call.base";
|
||||
import { BehaviorSubject, Observable, of } from "rxjs";
|
||||
import { Authorization } from "@services/authorization/authorization";
|
||||
import { inject } from "@angular/core";
|
||||
import { IOType } from "@models/node-types";
|
||||
|
||||
export class FlowsCallServiceFake extends FlowsCallServiceBase {
|
||||
override getFlowById(flowId: string): Observable<Flow> {
|
||||
|
|
@ -16,9 +17,10 @@ export class FlowsCallServiceFake extends FlowsCallServiceBase {
|
|||
authorizationService = inject(Authorization);
|
||||
|
||||
private data: Record<string, Flow> = {
|
||||
'1': { id: '1', name: 'A Flow', data: new FlowData(), visibility: 'public', author: 'Alice', createdAt: new Date("December 17, 2023 03:24:00"), updatedAt: new Date("January 7, 2026 12:24:00") },
|
||||
'2': { id: '2', name: 'Test Flow', data: new FlowData(), visibility: 'private', author: 'Bob', createdAt: new Date("April 25, 2025 12:24:00"), updatedAt: new Date("April 27, 2025 18:42:00") },
|
||||
};
|
||||
'1': { id: '1', name: 'A Flow', data: { nodes: [], connections: [] }, visibility: 'PUBLIC', author: 'Alice', createdAt: new Date("December 17, 2023 03:24:00"), updatedAt: new Date("January 7, 2026 12:24:00") },
|
||||
'2': { id: '2', name: 'Test Flow', data: { nodes: [], connections: [] }, visibility: 'PRIVATE', author: 'Bob', createdAt: new Date("April 25, 2025 12:24:00"), updatedAt: new Date("April 27, 2025 18:42:00") },
|
||||
'testFlow': flowFromJson(testDataFlow)
|
||||
}
|
||||
|
||||
override retrieveAllFlows() {
|
||||
return of(Object.values(this.data));
|
||||
|
|
@ -31,7 +33,7 @@ export class FlowsCallServiceFake extends FlowsCallServiceBase {
|
|||
|
||||
override createNewFlow(name?: string): Observable<Flow> {
|
||||
const newId = (Object.keys(this.data).length + 1).toString();
|
||||
this.data[newId] = { id: newId, name: name || `New Flow`, data: new FlowData() , visibility: 'private', author: this.authorizationService.loggedInUser()!.username, createdAt: new Date(), updatedAt: new Date() };
|
||||
this.data[newId] = { id: newId, name: name || `New Flow`, data: { nodes: [], connections: [] }, visibility: 'PRIVATE', author: this.authorizationService.loggedInUser()!.username, createdAt: new Date(), updatedAt: new Date() };
|
||||
return of(this.data[newId]);
|
||||
}
|
||||
|
||||
|
|
@ -40,3 +42,125 @@ export class FlowsCallServiceFake extends FlowsCallServiceBase {
|
|||
return of(void 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function flowFromJson(raw: any): Flow {
|
||||
return {
|
||||
id: raw.id,
|
||||
author: raw.author,
|
||||
visibility: raw.visibility as FlowVisibility,
|
||||
createdAt: new Date(raw.createdAt),
|
||||
updatedAt: new Date(raw.updatedAt),
|
||||
name: raw.name,
|
||||
description: raw.description,
|
||||
data: {
|
||||
nodes: raw.nodes,
|
||||
connections: raw.connections
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const testDataFlow ={
|
||||
"id": "my test flow",
|
||||
"author": "lucio.lelii",
|
||||
"visibility": "PUBLIC",
|
||||
"createdAt": "2026-01-21T10:38:50.671+00:00",
|
||||
"updatedAt": "2026-01-21T10:38:50.671+00:00",
|
||||
"name": "Test Flow",
|
||||
"description": "This is a test flow",
|
||||
"nodes": [
|
||||
{
|
||||
"key": "3ec4a0e5-914a-41e0-ab74-d4e64a4dba09",
|
||||
"name": "Input Node",
|
||||
"position": null,
|
||||
"parameters": null,
|
||||
"nodeDefinition": {
|
||||
"category": "Input",
|
||||
"runtimeParameters": null,
|
||||
"fixedParameters": null,
|
||||
"inputs": {},
|
||||
"outputs": {
|
||||
"phrase": {
|
||||
"type": "TEXT",
|
||||
"multiple": false
|
||||
}
|
||||
},
|
||||
"simulable": false,
|
||||
"name": "Phrase"
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "80519c40-20ba-49cf-800a-598ee2e64f65",
|
||||
"name": "translator to french",
|
||||
"position": null,
|
||||
"parameters": {
|
||||
"LLMProvider": "OllamaTestProvider",
|
||||
"LLMModel": "sam860/gemma3:270m"
|
||||
},
|
||||
"nodeDefinition": {
|
||||
"category": "Execution",
|
||||
"executor": "genericAIExecutorTest",
|
||||
"inputMappers": {
|
||||
"prompt": {
|
||||
"type": "translator",
|
||||
"translation": "translate the following phrase : \"${{phrase}}\" in french, return only the translation"
|
||||
}
|
||||
},
|
||||
"outputMappers": {
|
||||
"translated": {
|
||||
"type": "direct",
|
||||
"fieldName": "response"
|
||||
}
|
||||
},
|
||||
"name": "French Translator",
|
||||
"runtimeParameters": null,
|
||||
"fixedParameters": null,
|
||||
"inputs": {
|
||||
"phrase": {
|
||||
"type": "TEXT",
|
||||
"multiple": false
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"translated": {
|
||||
"type": "TEXT",
|
||||
"multiple": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"key": "ff349078-dce1-4ecd-8b2f-bd43100633fc",
|
||||
"name": "Output Node",
|
||||
"position": null,
|
||||
"parameters": null,
|
||||
"nodeDefinition": {
|
||||
"category": "Output",
|
||||
"runtimeParameters": null,
|
||||
"fixedParameters": null,
|
||||
"outputs": {},
|
||||
"inputs": {
|
||||
"translated": {
|
||||
"type": "TEXT",
|
||||
"multiple": false
|
||||
}
|
||||
},
|
||||
"name": "Translated Phrase"
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"key": "c335e3da-2af3-4e8f-a8ee-9bfc236196ee",
|
||||
"from": "3ec4a0e5-914a-41e0-ab74-d4e64a4dba09:phrase",
|
||||
"to": "80519c40-20ba-49cf-800a-598ee2e64f65:phrase"
|
||||
},
|
||||
{
|
||||
"key": "e0239aef-d570-463d-b453-c3906ab0e4b0",
|
||||
"from": "80519c40-20ba-49cf-800a-598ee2e64f65:translated",
|
||||
"to": "ff349078-dce1-4ecd-8b2f-bd43100633fc:translated"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
@ -5,8 +5,8 @@
|
|||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="h-screen flex flex-col">
|
||||
<div class="w-full max-w-sm py-2">
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="w-full max-w-sm py-2 h-15">
|
||||
<div class="relative flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
||||
class="absolute w-5 h-5 top-2.5 left-2.5 text-slate-600">
|
||||
|
|
@ -21,26 +21,26 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group w-full mb-3" role="group">
|
||||
<div class="btn-group w-full mb-3 h-8" role="group">
|
||||
|
||||
<button type="button" class="btn btn-sm w-full transition" [class.btn-primary]="filter() === 'all'"
|
||||
[class.btn-outline-primary]="filter() !== 'all'" (click)="filter.set('all')">
|
||||
All
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-sm w-full transition" [class.btn-primary]="filter() === 'public'"
|
||||
[class.btn-outline-primary]="filter() !== 'public'" (click)="filter.set('public')">
|
||||
<button type="button" class="btn btn-sm w-full transition" [class.btn-primary]="filter() === 'PUBLIC'"
|
||||
[class.btn-outline-primary]="filter() !== 'PUBLIC'" (click)="filter.set('PUBLIC')">
|
||||
Public
|
||||
</button>
|
||||
|
||||
<button type="button" class="btn btn-sm w-full transition" [class.btn-primary]="filter() === 'private'"
|
||||
[class.btn-outline-primary]="filter() !== 'private'" (click)="filter.set('private')">
|
||||
<button type="button" class="btn btn-sm w-full transition" [class.btn-primary]="filter() === 'PRIVATE'"
|
||||
[class.btn-outline-primary]="filter() !== 'PRIVATE'" (click)="filter.set('PRIVATE')">
|
||||
Private
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex pt-2 pb-1 text-sm w-full items-center text-center justify-center">
|
||||
<div class="flex py-2 text-sm w-full items-center text-center justify-center h-18">
|
||||
<app-ordering [orderFields]="orderFields" [orderView]="orderView" (orderChanged)="onOrderChanged($event)" ></app-ordering>
|
||||
</div>
|
||||
@if (filteredFlows().length === 0) {
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
No flows found
|
||||
</div>
|
||||
} @else {
|
||||
<div class="flex-1 overflow-y-auto h-full w-full py-2 gap-2 flex flex-col items-stretch max-h-[calc(100vh-32rem)]">
|
||||
<div class="flex-1 overflow-y-auto w-full h-full py-2 gap-2 flex flex-col items-stretch max-h-[calc(100vh-380px)]">
|
||||
@for (flow of filteredFlows(); track flow.id) {
|
||||
<app-flow-item [(detailOpenedId)]="detailOpenedId" [flow]="flow"></app-flow-item>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { CommonModule } from '@angular/common';
|
||||
import { Component, computed, effect, inject, model, output, signal, Signal, WritableSignal } from '@angular/core';
|
||||
import { Flow } from '@models/flow';
|
||||
import { Flow, FlowVisibility } from '@models/flow';
|
||||
import { FlowsService } from '@services/flows/flows';
|
||||
import { FlowItem } from './flow-item/flow-item';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { OrderEvent, OrderField, Ordering } from "@shared/ordering/ordering";
|
||||
import { ListStateViewHolder, OrderViewState } from '@utilities/list-state-holder';
|
||||
|
||||
type FlowFilter = 'all' | 'public' | 'private';
|
||||
type FlowFilter = FlowVisibility | 'all';
|
||||
|
||||
@Component({
|
||||
selector: 'app-flows-list',
|
||||
|
|
@ -18,7 +18,6 @@ type FlowFilter = 'all' | 'public' | 'private';
|
|||
export class FlowsList extends ListStateViewHolder<Flow> {
|
||||
filter = signal<FlowFilter>('all');
|
||||
|
||||
|
||||
orderFields: OrderField[] = [
|
||||
{ field: 'name', label: 'Name' },
|
||||
{ field: 'updatedAt', label: 'Last Update Date' },
|
||||
|
|
@ -30,7 +29,6 @@ export class FlowsList extends ListStateViewHolder<Flow> {
|
|||
private flowsService = inject(FlowsService);
|
||||
|
||||
loading: WritableSignal<boolean> = signal(true);
|
||||
|
||||
|
||||
get orderView() {
|
||||
const existingState = this.view;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
class="w-full flex items-center justify-between
|
||||
px-4 py-3
|
||||
text-sm font-semibold text-gray-700 user-select-none
|
||||
bg-gray-50 "
|
||||
bg-gray-50 h-18"
|
||||
>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
</div>
|
||||
<!-- Body -->
|
||||
<div
|
||||
class="transition-all duration-300 px-2 pb-2 flex flex-col flex-1 overflow-hidden">
|
||||
class="transition-all duration-300 px-2 pb-2 flex flex-col flex-1 overflow-hidden h-[calc(100%-72px)]">
|
||||
<ng-content></ng-content>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,22 +3,21 @@ import {
|
|||
Input,
|
||||
HostBinding,
|
||||
ChangeDetectorRef,
|
||||
OnChanges
|
||||
} from "@angular/core";
|
||||
import { ClassicPreset } from "rete";
|
||||
import { CommonModule, KeyValue } from "@angular/common";
|
||||
import { ReteModule } from "rete-angular-plugin/21";
|
||||
import { HFNode } from "@models/nodes";
|
||||
|
||||
@Component({
|
||||
templateUrl: "./input-node.component.html",
|
||||
styleUrls: ["./input-node.component.scss"],
|
||||
imports: [CommonModule, ReteModule],
|
||||
host: {
|
||||
"data-testid": "input-node"
|
||||
"data-testid": "node"
|
||||
}
|
||||
})
|
||||
export class InputNodeComponent implements OnChanges {
|
||||
@Input() data!: ClassicPreset.Node;
|
||||
export class InputNodeComponent {
|
||||
@Input() data!: any;
|
||||
@Input() emit!: (data: any) => void;
|
||||
@Input() rendered!: () => void;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
import { ClassicPreset } from "rete";
|
||||
import { CommonModule, KeyValue } from "@angular/common";
|
||||
import { ReteModule } from "rete-angular-plugin/21";
|
||||
import { HFNode } from "@models/nodes";
|
||||
|
||||
@Component({
|
||||
templateUrl: "./output-node.component.html",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { Component, ElementRef, Injector, ViewChild } from '@angular/core';
|
||||
import { Component, ElementRef, Injector, Input, input, output, ViewChild } from '@angular/core';
|
||||
import { FlowData } from '@models/flow';
|
||||
import { EditorStateHolder } from '@stores/flow-editor';
|
||||
import { createEditor } from '@utilities/rete-editor';
|
||||
|
||||
@Component({
|
||||
|
|
@ -9,15 +11,32 @@ import { createEditor } from '@utilities/rete-editor';
|
|||
})
|
||||
export class ReteEditor {
|
||||
|
||||
constructor(private injector: Injector) {}
|
||||
flowData: FlowData;
|
||||
|
||||
constructor(private injector: Injector, private flowState: EditorStateHolder ) {
|
||||
this.flowData = this.flowState.currentFlow()!.data;
|
||||
}
|
||||
|
||||
@ViewChild("editor") container!: ElementRef;
|
||||
|
||||
|
||||
flowChanged = output<any>();
|
||||
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
const el = this.container.nativeElement;
|
||||
|
||||
if (el) {
|
||||
createEditor(el, this.injector);
|
||||
createEditor(el, this.injector, this.flowData).then(editor => {
|
||||
console.log("Rete editor created:", editor);
|
||||
editor.addPipe(
|
||||
(context) => {
|
||||
this.flowChanged.emit({});
|
||||
return context;
|
||||
}
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import {
|
|||
import { AngularPlugin, Presets, AngularArea2D, NodeComponent } from "rete-angular-plugin/21";
|
||||
import { InputNodeComponent } from "@shared/nodes/input/input-node.component";
|
||||
import { OutputNodeComponent } from "@shared/nodes/output/output-node.component";
|
||||
import { flowTest } from "@models/test";
|
||||
import { HFSchemes } from "@models/nodes";
|
||||
import { FlowData } from "@models/flow";
|
||||
|
||||
type Schemes = GetSchemes<
|
||||
ClassicPreset.Node,
|
||||
|
|
@ -16,8 +17,7 @@ type Schemes = GetSchemes<
|
|||
>;
|
||||
type AreaExtra = AngularArea2D<Schemes>;
|
||||
|
||||
export async function createEditor(container: HTMLElement, injector: Injector, flowData?: any) {
|
||||
const socket = new ClassicPreset.Socket("socket");
|
||||
export async function createEditor(container: HTMLElement, injector: Injector, flowData: FlowData) {
|
||||
|
||||
const editor = new NodeEditor<Schemes>();
|
||||
const area = new AreaPlugin<Schemes, AreaExtra>(container);
|
||||
|
|
@ -29,22 +29,28 @@ export async function createEditor(container: HTMLElement, injector: Injector, f
|
|||
});
|
||||
|
||||
render.addPreset(
|
||||
Presets.classic.setup({
|
||||
customize: {
|
||||
node(context) {
|
||||
if (context.payload.label === "Input") {
|
||||
return InputNodeComponent;
|
||||
}
|
||||
if (context.payload.label === "Output") {
|
||||
return OutputNodeComponent;
|
||||
}
|
||||
Presets.classic.setup({
|
||||
customize: {
|
||||
node(context) {
|
||||
if (context.payload.label === "Input") {
|
||||
return InputNodeComponent;
|
||||
}
|
||||
if (context.payload.label === "Output") {
|
||||
return OutputNodeComponent;
|
||||
}
|
||||
|
||||
return NodeComponent;
|
||||
return NodeComponent;
|
||||
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
area.addPipe((c) => {
|
||||
if (c.type === "render") console.log(c.data);
|
||||
return c;
|
||||
});
|
||||
|
||||
connection.addPreset(ConnectionPresets.classic.setup());
|
||||
|
||||
|
|
@ -54,35 +60,85 @@ export async function createEditor(container: HTMLElement, injector: Injector, f
|
|||
|
||||
AreaExtensions.simpleNodesOrder(area);
|
||||
|
||||
if (flowTest) {
|
||||
for (const nodeData of flowTest.nodes) {
|
||||
const node = new ClassicPreset.Node(nodeData.nodeDefinition?.category || "Undefined");
|
||||
node.id = nodeData.key;
|
||||
Object.entries(nodeData.nodeDefinition?.outputs).forEach(([output, def]) => {
|
||||
node.addOutput(output,
|
||||
new ClassicPreset.Output(
|
||||
socket, output
|
||||
)
|
||||
);
|
||||
});
|
||||
Object.entries(nodeData.nodeDefinition?.inputs).forEach(([input, def]) => {
|
||||
console.log("Adding input", input);
|
||||
node.addInput(input,
|
||||
new ClassicPreset.Input(
|
||||
socket, input
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
await editor.addNode(node);
|
||||
/*if (nodeData.position != null)
|
||||
area.translate(node.id, { x: nodeData.position.x, y: nodeData.position.y });*/
|
||||
}
|
||||
}
|
||||
if (flowData)
|
||||
await loadFlowData(editor, flowData);
|
||||
|
||||
//await editor.addConnection(new ClassicPreset.Connection(a, "a", b, "b"));
|
||||
|
||||
AreaExtensions.zoomAt(area, editor.getNodes());
|
||||
|
||||
return () => area.destroy();
|
||||
|
||||
return editor;
|
||||
}
|
||||
|
||||
export function exportGraph(editor: NodeEditor<HFSchemes>) {
|
||||
const nodes = editor.getNodes().map(schema => ({
|
||||
|
||||
}));
|
||||
|
||||
const connections = editor.getConnections().map(c => ({
|
||||
id: c.id,
|
||||
from: {
|
||||
nodeId: c.source,
|
||||
output: c.sourceOutput
|
||||
},
|
||||
to: {
|
||||
nodeId: c.target,
|
||||
input: c.targetInput
|
||||
}
|
||||
}));
|
||||
|
||||
return {
|
||||
nodes,
|
||||
connections
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async function loadFlowData(editor: NodeEditor<HFSchemes>, flowData: FlowData) {
|
||||
if (flowData.nodes.length === 0) return;
|
||||
const socket = new ClassicPreset.Socket("socket");
|
||||
const nodeMapping = new Map<string, string>();
|
||||
for (const nodeData of flowData.nodes) {
|
||||
const node = new ClassicPreset.Node(nodeData.nodeDefinition?.category || "Undefined");
|
||||
nodeMapping.set(nodeData.key, node.id);
|
||||
Object.entries(nodeData.nodeDefinition?.outputs).forEach(([output, def]) => {
|
||||
console.log("Adding output", output);
|
||||
node.addOutput(output,
|
||||
new ClassicPreset.Output(
|
||||
socket, output
|
||||
)
|
||||
);
|
||||
});
|
||||
Object.entries(nodeData.nodeDefinition?.inputs).forEach(([input, def]) => {
|
||||
console.log("Adding input", input);
|
||||
node.addInput(input,
|
||||
new ClassicPreset.Input(
|
||||
socket, input
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
await editor.addNode(node);
|
||||
/*if (nodeData.position != null)
|
||||
area.translate(node.id, { x: nodeData.position.x, y: nodeData.position.y });*/
|
||||
}
|
||||
|
||||
for (const connections of flowData.connections) { {
|
||||
const targetNode = editor.getNode(nodeMapping.get(connections.targetNode) || "");
|
||||
const node = editor.getNode(nodeMapping.get(connections.sourceNode) || "");
|
||||
console.log("Creating connection from", connections.sourceNode, "to", connections.targetNode);
|
||||
if (node && targetNode) {
|
||||
const connection = new ClassicPreset.Connection(
|
||||
node,
|
||||
connections.sourceField,
|
||||
targetNode,
|
||||
connections.targetField
|
||||
);
|
||||
await editor.addConnection(connection);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue