Inputs
diff --git a/src/app/shared/nodes/container-node/container-node.ts b/src/app/shared/nodes/container-node/container-node.ts
index 2e9bc34..e155317 100644
--- a/src/app/shared/nodes/container-node/container-node.ts
+++ b/src/app/shared/nodes/container-node/container-node.ts
@@ -1,5 +1,6 @@
import { CommonModule } from '@angular/common';
import { ChangeDetectorRef, Component, HostBinding, Input, inject } from '@angular/core';
+import { FormsModule } from '@angular/forms';
import { MatTooltipModule } from '@angular/material/tooltip';
import { currentFlowPortValueKind, flowValueKindLabel, FlowBlock, FlowContainer, FlowData, FLOW_DEPENDANT_PORT_KEY, FLOW_DEPENDENCY_PORT_KEY } from '@models/flow';
import { NodeSettingField, NodeSettingOption, NodeSettingsDialogService } from '@services/dialogs/node-settings-dialog';
@@ -62,7 +63,7 @@ type StructuredRetrieverConfig = {
@Component({
selector: 'app-container-node',
- imports: [CommonModule, ReteModule, MatTooltipModule],
+ imports: [CommonModule, FormsModule, ReteModule, MatTooltipModule],
templateUrl: './container-node.html',
styleUrl: './container-node.css',
host: {
@@ -86,6 +87,8 @@ export class ContainerNodeComponent {
parameterFields: ContainerFieldView[] = [];
richContentFields: RichContentView[] = [];
schemaReady = false;
+ nameEditorOpen = false;
+ draftName = '';
@Input() data!: any;
@Input() emit!: (data: any) => void;
@@ -223,17 +226,22 @@ export class ContainerNodeComponent {
return requiredFields
.filter((field, index, fields) => fields.findIndex((candidate) => candidate.path === field.path) === index)
- .filter((field) => field.path !== 'name' && field.path !== 'subFlow')
+ .filter((field) => field.path !== 'name')
+ .filter((field) => field.path !== 'subFlow')
+ .filter((field) => !field.path.startsWith('subFlow.'))
.filter((field) => this.isFieldEnabled(field.path))
.filter((field) => this.isMissingValue(getValueByPath(config, field.path)))
.map((field) => field.label)
.concat(
this.schemaRequirements.requiredObjects
- .filter((field) => field.path !== 'name' && field.path !== 'subFlow')
+ .filter((field) => field.path !== 'name')
+ .filter((field) => field.path !== 'subFlow')
+ .filter((field) => !field.path.startsWith('subFlow.'))
.filter((field) => this.isFieldEnabled(field.path))
.filter((field) => this.isMissingValue(getValueByPath(config, field.path)))
.map((field) => field.label)
)
+ .concat(this.subFlow ? [] : ['Subflow'])
.filter((field, index, fields) => fields.indexOf(field) === index);
}
@@ -353,6 +361,49 @@ export class ContainerNodeComponent {
}
}
+ openNameEditor(event?: Event) {
+ event?.preventDefault();
+ event?.stopPropagation();
+ if (this.isReadonly) return;
+ this.draftName = this.name;
+ this.nameEditorOpen = true;
+ }
+
+ cancelNameEditor(event?: Event) {
+ event?.preventDefault();
+ event?.stopPropagation();
+ this.nameEditorOpen = false;
+ this.draftName = this.name;
+ }
+
+ onDraftNameChange(value: string) {
+ this.draftName = value;
+ }
+
+ saveNameEditor(event?: Event) {
+ event?.preventDefault();
+ event?.stopPropagation();
+ if (this.isReadonly) return;
+
+ const nextName = this.draftName.trim().slice(0, 20);
+ if (!nextName || nextName === this.name) {
+ this.cancelNameEditor();
+ return;
+ }
+
+ const nextConfiguration = this.cloneConfiguration();
+ nextConfiguration['name'] = nextName;
+ this.data.data = {
+ ...this.data.data,
+ name: nextName,
+ specificConfiguration: nextConfiguration
+ };
+ this.updateCurrentFlowData(nextConfiguration);
+ this.refreshParameterFields();
+ this.refreshView();
+ this.nameEditorOpen = false;
+ }
+
async openParameterEditor(path: string, event?: Event) {
event?.preventDefault();
event?.stopPropagation();
@@ -660,6 +711,7 @@ export class ContainerNodeComponent {
.filter((field) => field.parts.length > 0);
this.parameterFields = this.containerFieldDefinitions
+ .filter((field) => !this.isContainerTypeField(field.path))
.filter((field) => this.isFieldVisible(field.path))
.filter((field) => !richContentPaths.has(field.path))
.map((field) => ({
@@ -689,7 +741,7 @@ export class ContainerNodeComponent {
const childResolved = resolveSchemaRef(childSchema as Record
, schema);
if (shouldSkipSchemaField(key, childResolved)) continue;
const path = pathPrefix ? `${pathPrefix}.${key}` : key;
- if (path === 'name' || path === 'subFlow') continue;
+ if (path === 'name' || path === 'subFlow' || this.isContainerTypeField(path)) continue;
const hasChildren = !!childResolved?.properties || childResolved?.type === 'object';
if (hasChildren) {
@@ -720,10 +772,21 @@ export class ContainerNodeComponent {
private richContentPaths(): string[] {
return this.containerFieldDefinitions
+ .filter((field) => !this.isContainerTypeField(field.path))
.filter((field) => field.widget === 'textarea')
.map((field) => field.path);
}
+ private isContainerTypeField(path: string): boolean {
+ return [
+ 'type',
+ 'typeName',
+ 'containerType',
+ 'configurationType',
+ 'configurationClass'
+ ].some((key) => path === key || path.endsWith(`.${key}`));
+ }
+
private toRichContentParts(path: string): { text: string; isDynamicInput: boolean }[] {
const content = String(getValueByPath(this.configuration ?? {}, path) ?? '').trim();
if (!content) return [];
diff --git a/src/app/shared/nodes/task-step-node/task-step-node.ts b/src/app/shared/nodes/task-step-node/task-step-node.ts
index 694669c..fe6ccb6 100644
--- a/src/app/shared/nodes/task-step-node/task-step-node.ts
+++ b/src/app/shared/nodes/task-step-node/task-step-node.ts
@@ -890,7 +890,20 @@ export class TaskStepNodeComponent {
}
private shouldHideConfigPath(path: string): boolean {
- return this.isContainerNode() && (path === 'subFlow' || path.startsWith('subFlow.'));
+ const isContainerTypePath = [
+ 'type',
+ 'typeName',
+ 'containerType',
+ 'configurationType',
+ 'configurationClass'
+ ].some((key) => path === key || path.endsWith(`.${key}`));
+
+ return this.isContainerNode()
+ && (
+ isContainerTypePath
+ || path === 'subFlow'
+ || path.startsWith('subFlow.')
+ );
}
private toArrayFieldItems(definition: ArrayFieldDefinition, value: unknown): ArrayFieldItemView[] {
diff --git a/src/app/shared/task-execution-viewer/task-execution-viewer.css b/src/app/shared/task-execution-viewer/task-execution-viewer.css
index 96acd41..bc924fe 100644
--- a/src/app/shared/task-execution-viewer/task-execution-viewer.css
+++ b/src/app/shared/task-execution-viewer/task-execution-viewer.css
@@ -186,10 +186,13 @@
}
.execution-log-icon {
- width: 18px;
- height: 18px;
+ flex: 0 0 auto;
+ width: 20px;
+ height: 20px;
font-size: 18px;
color: #475569;
+ line-height: 20px;
+ overflow: visible;
margin-top: 1px;
}
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index 8ad6e5e..9cdf58e 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -9,7 +9,7 @@ import { TaskExecutionsCallService } from "@services/task-executions/task-execut
export const environment = {
apiUrl: '/api',
production: true,
- assistantEnabled: false,
+ assistantEnabled: true,
tourModeAlwaysOn: false,
turnstileEnabled: true,
authorizationCallService: AuthorizationCallService,