feat(editor): name connected storage resources

This commit is contained in:
Lucio Lelii 2026-09-30 08:23:55 +02:00
parent 76fe788a16
commit aeead4bc6c
8 changed files with 38 additions and 6 deletions

View File

@ -1082,6 +1082,8 @@
border-color: #14b8a6;
background: linear-gradient(135deg, #ccfbf1 0%, #99f6e4 100%);
box-shadow: inset 0 0 0 1px rgba(13, 148, 136, 0.14);
letter-spacing: 0.04em;
text-transform: none;
}
.llm-pill-resource--connected .bi {

View File

@ -231,7 +231,7 @@
refComponent
class="llm-pill llm-pill-input llm-pill-resource"
[class.llm-pill-resource--connected]="resourceLinkIsConnected"
[attr.title]="resourceLinkIsConnected ? 'Resource connected — drag another Resource here to replace it' : 'Drag a Resource here'"
[attr.title]="resourceLinkIsConnected ? 'Connected to ' + connectedResourceName + ' — drag another Resource here to replace it' : 'Drag a Resource here'"
[data]="{
type: 'socket',
side: 'input',
@ -243,7 +243,7 @@
[emit]="emit">
@if (resourceLinkIsConnected) {
<i class="bi bi-link-45deg" aria-hidden="true"></i>
Resource connected
Connected to {{ connectedResourceName }}
} @else {
Resource
}

View File

@ -283,6 +283,16 @@ export class GenericNodeComponent implements OnDestroy {
return typeof value === 'string' && value.trim().length > 0;
}
/** The Resource node named by the handle, retained by the canvas when the link is drawn. */
get connectedResourceName(): string {
const nodeData = this.data?.data as Record<string, any> | undefined;
const name = nodeData?.['__connectedResourceName'];
if (typeof name === 'string' && name.trim()) return name.trim();
const field = nodeData?.['__resourceRefField'];
const id = typeof field === 'string' ? nodeData?.['specificConfiguration']?.[field] : null;
return typeof id === 'string' && id.trim() ? id : 'Resource';
}
get dependantOutput(): RenderedSocketPort | null {
const output = this.data?.outputs?.[FLOW_DEPENDANT_PORT_KEY];
return output

View File

@ -885,6 +885,8 @@ button.llm-node-bias-summary:focus-visible {
border-color: #14b8a6;
background: linear-gradient(135deg, #ccfbf1 0%, #99f6e4 100%);
box-shadow: inset 0 0 0 1px rgba(13, 148, 136, 0.14);
letter-spacing: 0.04em;
text-transform: none;
}
.llm-pill-resource--connected .bi {

View File

@ -259,7 +259,7 @@
refComponent
class="llm-pill llm-pill-input llm-pill-resource"
[class.llm-pill-resource--connected]="storageLinkIsConnected"
[attr.title]="storageLinkIsConnected ? 'Resource connected' : 'Resource'"
[attr.title]="storageLinkIsConnected ? 'Connected to ' + connectedResourceName : 'Resource'"
[data]="{
type: 'socket',
side: 'input',
@ -271,7 +271,7 @@
[emit]="emit">
@if (storageLinkIsConnected) {
<i class="bi bi-link-45deg" aria-hidden="true"></i>
Resource connected
Connected to {{ connectedResourceName }}
} @else {
Resource
}

View File

@ -181,6 +181,16 @@ export class TaskStepNodeComponent {
return typeof value === 'string' && value.trim().length > 0;
}
/** The Resource node named by the handle, retained by the canvas when the link is drawn. */
get connectedResourceName(): string {
const nodeData = this.data?.data as Record<string, any> | undefined;
const name = nodeData?.['__connectedResourceName'];
if (typeof name === 'string' && name.trim()) return name.trim();
const field = nodeData?.['__resourceRefField'];
const id = typeof field === 'string' ? nodeData?.['specificConfiguration']?.[field] : null;
return typeof id === 'string' && id.trim() ? id : 'Resource';
}
get dependantOutput(): { key: string; socket: ClassicPreset.Socket } | null {
const output = this.data?.outputs?.[FLOW_DEPENDANT_PORT_KEY];
return output

View File

@ -97,7 +97,7 @@ describe('the link between a resource and a step that uses it', () => {
s3.data = { id: 's3', typeName: 'S3Storage', nodeFamily: 'resource' };
s3.addOutput(FLOW_RESOURCE_PORT_KEY, new ClassicPreset.Output(socket));
const postgres = new ClassicPreset.Node('PostgreSQL') as any;
postgres.data = { id: 'postgres', typeName: 'PostgresStorage', nodeFamily: 'resource' };
postgres.data = { id: 'postgres', name: 'Analytics database', typeName: 'PostgresStorage', nodeFamily: 'resource' };
postgres.addOutput(FLOW_RESOURCE_PORT_KEY, new ClassicPreset.Output(socket));
const operation = new ClassicPreset.Node('Operation') as any;
operation.data = {
@ -124,6 +124,7 @@ describe('the link between a resource and a step that uses it', () => {
expect(operation.data.specificConfiguration).toMatchObject({
storageNode: 'postgres', resourceType: 'PostgresStorage', operationId: null, settings: {}
});
expect(operation.data.__connectedResourceName).toBe('Analytics database');
expect(area.update).toHaveBeenCalledWith('node', operation.id);
});

View File

@ -872,10 +872,17 @@ export async function syncResourceLinkPresentation(
const resourceType = typeof source?.data?.typeName === 'string' && source.data.typeName.trim()
? source.data.typeName
: null;
const resourceName = typeof source?.data?.name === 'string' && source.data.name.trim()
? source.data.name
: typeof source?.data?.specificConfiguration?.name === 'string' && source.data.specificConfiguration.name.trim()
? source.data.specificConfiguration.name
: resourceId;
const configuration = targetData['specificConfiguration'] as Record<string, unknown> | undefined;
const resourceChanged = configuration?.[reference.field] !== resourceId;
if (!resourceChanged && configuration?.['resourceType'] === resourceType) return;
const nameChanged = targetData['__connectedResourceName'] !== resourceName;
if (!resourceChanged && configuration?.['resourceType'] === resourceType && !nameChanged) return;
targetData['__connectedResourceName'] = resourceName;
targetData['specificConfiguration'] = {
...(configuration ?? {}),
[reference.field]: resourceId,