From 52b26acdddde576632cab6b70d7c8eb09cf7e5b1 Mon Sep 17 00:00:00 2001 From: Lucio Lelii Date: Tue, 29 Sep 2026 08:58:23 +0200 Subject: [PATCH] Describe a resource node from the resource catalog, not the block one The resource node inherited the generic node's schema lookup, which asks the block catalog. Storage is not a block, so the lookup found nothing: the node showed no fields, and every click on it fetched the catalog again and covered it with the loading overlay, so nothing inside could be clicked. The lookup is now a hook the resource node overrides with the resource catalog, and a resource is never recreated on the server - it has no ports for the server to recompute. Co-Authored-By: Claude Opus 5.5 --- .../shared/nodes/generic-node/generic-node.ts | 16 ++++++++++- .../nodes/resource-node/resource-node.ts | 28 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/app/shared/nodes/generic-node/generic-node.ts b/src/app/shared/nodes/generic-node/generic-node.ts index 774bb0a..b712005 100644 --- a/src/app/shared/nodes/generic-node/generic-node.ts +++ b/src/app/shared/nodes/generic-node/generic-node.ts @@ -856,7 +856,7 @@ export class GenericNodeComponent implements OnDestroy { this.schemaLoading = true; this.schemaReady = false; try { - const blockType = this.blocksService.peekBlockType(type) ?? await this.blocksService.getBlockType(type); + const blockType = await this.resolveNodeDescriptor(type); this.blockDescriptor = blockType ?? null; this.blockSchema = (blockType?.schema ?? null) as Record | null; this.schemaRequirements = extractSchemaRequirements(this.blockSchema); @@ -882,6 +882,19 @@ export class GenericNodeComponent implements OnDestroy { } } + /** Where this node's type is described: the block catalog here, another one for another kind of node. */ + protected async resolveNodeDescriptor(type: string): Promise { + return this.blocksService.peekBlockType(type) ?? await this.blocksService.getBlockType(type) ?? null; + } + + /** + * A block is recreated on the server when a field that shapes its ports changes. A node with no + * ports has nothing for the server to recompute. + */ + protected get createsOnServer(): boolean { + return true; + } + private shouldRetrySchemaLoad(): boolean { if (this.schemaLoading) return false; if (!this.blockType) return false; @@ -2319,6 +2332,7 @@ export class GenericNodeComponent implements OnDestroy { } private maybeCreateBlockOnServer() { + if (!this.createsOnServer) return; const nodeData = this.data?.data as Record | undefined; if (!nodeData?.['__needsServerCreate']) return; if (nodeData['__isCreatingOnServer'] === true) return; diff --git a/src/app/shared/nodes/resource-node/resource-node.ts b/src/app/shared/nodes/resource-node/resource-node.ts index 2cc1827..dd2882f 100644 --- a/src/app/shared/nodes/resource-node/resource-node.ts +++ b/src/app/shared/nodes/resource-node/resource-node.ts @@ -3,9 +3,11 @@ // Attribution term under AGPL-3.0 section 7(b): see LICENSE-ADDENDUM. import { CommonModule } from '@angular/common'; -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BlockType, RESOURCE_NODE_CAPABILITIES } from '@models/flow'; +import { ResourcesService } from '@services/resources/resources'; import { ReteModule } from 'rete-angular-plugin/21'; import { GenericNodeComponent } from '../generic-node/generic-node'; import { NodeParametersComponent } from '../node-parameters/node-parameters'; @@ -29,6 +31,30 @@ import { NodeParametersComponent } from '../node-parameters/node-parameters'; }) export class ResourceNodeComponent extends GenericNodeComponent { + private readonly resourcesService = inject(ResourcesService); + + /** Described by the resource catalog: the block catalog has never heard of it. */ + protected override async resolveNodeDescriptor(type: string): Promise { + const resourceType = this.resourcesService.peekResourceType(type) + ?? (await this.resourcesService.getAllResourceTypes())().find((candidate) => candidate.type === type); + if (!resourceType) return null; + return { + type: resourceType.type, + family: 'resource', + description: resourceType.description, + userInteractive: false, + configurationType: resourceType.configurationType, + configurationClass: resourceType.configurationClass, + schema: resourceType.schema, + capabilities: RESOURCE_NODE_CAPABILITIES + }; + } + + /** It has no ports, so there is nothing for the server to rebuild when a field changes. */ + protected override get createsOnServer(): boolean { + return false; + } + /** The type of resource, as the palette names it: what a block's title would say. */ resourceTitle(): string { return String(this.data?.data?.typeName ?? 'Resource');