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 <noreply@anthropic.com>
This commit is contained in:
Lucio Lelii 2026-09-29 08:58:23 +02:00
parent 612fe41515
commit 52b26acddd
2 changed files with 42 additions and 2 deletions

View File

@ -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<string, any> | 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<BlockType | null> {
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<string, unknown> | undefined;
if (!nodeData?.['__needsServerCreate']) return;
if (nodeData['__isCreatingOnServer'] === true) return;

View File

@ -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<BlockType | null> {
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');