ui: make node settings collapsible

This commit is contained in:
Lucio Lelii 2026-09-29 18:56:17 +02:00
parent d3f8ea3006
commit 44a4ff09a9
11 changed files with 79 additions and 8 deletions

View File

@ -219,7 +219,8 @@
}
.container-node__delete,
.container-node__focus {
.container-node__focus,
.container-node__settings {
border: 0;
width: 30px;
height: 30px;
@ -228,6 +229,11 @@
color: #f8fafc;
}
.container-node__settings--active {
background: rgba(255, 255, 255, 0.34);
color: #ffffff;
}
.container-node__clone {
border: 0;
width: 30px;

View File

@ -72,6 +72,18 @@
</div>
}
@if (!isReadonly) {
@if (!focusOpen && (hasParameterFields() || hasMainContent())) {
<button
type="button"
class="container-node__settings"
[class.container-node__settings--active]="nodeSettingsOpen"
[attr.aria-expanded]="nodeSettingsOpen"
[matTooltip]="nodeSettingsOpen ? 'Hide settings' : 'Show settings'"
(pointerdown)="$event.stopPropagation()"
(click)="toggleNodeSettings($event)">
<i class="bi bi-gear"></i>
</button>
}
<button type="button" class="container-node__focus" [matTooltip]="focusOpen ? 'Close expanded node' : 'Expand node'" (pointerdown)="$event.stopPropagation()" (click)="toggleFocus($event)">
<i class="bi" [class.bi-arrows-angle-expand]="!focusOpen" [class.bi-x-lg]="focusOpen"></i>
</button>
@ -219,7 +231,7 @@
</div>
}
@if (hasParameterFields() || hasMainContent()) {
@if ((hasParameterFields() || hasMainContent()) && (nodeSettingsOpen || focusOpen)) {
<div class="container-node__params container-node__params-shell">
@if (!schemaReady) {
<div class="container-node__skeleton-overlay" aria-hidden="true">

View File

@ -137,6 +137,8 @@ export class ContainerNodeComponent implements OnDestroy {
optionalGroupFieldDefinitions: SchemaOptionalGroupFieldDefinition[] = [];
parameterDisplaySections: ContainerDisplaySection[] = [];
schemaReady = false;
/** Settings stay out of the way on the canvas until the gear button opens them. */
nodeSettingsOpen = false;
focusOpen = false;
private schemaLoading = false;
nameEditorOpen = false;
@ -219,6 +221,13 @@ export class ContainerNodeComponent implements OnDestroy {
return this.data?.data?.['__readonly'] === true;
}
toggleNodeSettings(event?: Event) {
event?.preventDefault();
event?.stopPropagation();
this.nodeSettingsOpen = !this.nodeSettingsOpen;
this.cdr.markForCheck();
}
toggleFocus(event?: Event) {
event?.preventDefault();
event?.stopPropagation();

View File

@ -443,6 +443,11 @@
color: #1d4ed8;
}
.llm-clone-btn--active {
background: #dbeafe;
color: #1d4ed8;
}
.llm-node--human .llm-clone-btn:hover {
background: #fff7ed;
color: #c2410c;

View File

@ -108,6 +108,18 @@
</div>
}
@if (!isReadonly) {
@if (!focusOpen && hasOrderedParameterItems()) {
<button
type="button"
class="llm-clone-btn"
[class.llm-clone-btn--active]="nodeSettingsOpen"
[attr.aria-expanded]="nodeSettingsOpen"
[matTooltip]="nodeSettingsOpen ? 'Hide settings' : 'Show settings'"
(pointerdown)="$event.stopPropagation()"
(click)="toggleNodeSettings($event)">
<i class="bi bi-gear"></i>
</button>
}
<button
type="button"
class="llm-clone-btn"
@ -291,7 +303,7 @@
</div>
}
<app-node-parameters [host]="this" />
<app-node-parameters [host]="this" [showParameters]="nodeSettingsOpen || focusOpen" />
@if (biasAnnotationsAllowed) {
<app-bias-annotations

View File

@ -99,6 +99,16 @@ describe('GenericNodeComponent', () => {
await fixture.whenStable();
});
it('keeps the name editor available while settings are collapsed', () => {
expect(component.nodeSettingsOpen).toBe(false);
component.openNameEditor();
fixture.detectChanges();
expect(component.localEditorOpen).toBe(true);
expect(fixture.nativeElement.querySelector('.llm-modal-backdrop')).not.toBeNull();
});
describe('clearing an optional number', () => {
/** Opens the modal editor on a numeric path, as clicking the pen does. */
function openNumericEditor(path: string, current: unknown) {
@ -518,6 +528,7 @@ describe('GenericNodeComponent', () => {
item: { path: 'skills', field: null, richContentField: null, arrayField, optionalGroupField: null }
}
];
component.nodeSettingsOpen = true;
component.cdr.detectChanges();
// A list starts closed, showing only its count; opened, its rows offer the button.
for (const toggle of fixture.nativeElement.querySelectorAll('.llm-array-toggle')) {

View File

@ -235,6 +235,8 @@ export class GenericNodeComponent implements OnDestroy {
localEditorFreeText = false;
localEditorBindableInputName: string | null = null;
deleteConfirmOpen = false;
/** Keeps the canvas compact until the person explicitly needs to inspect or edit the fields. */
nodeSettingsOpen = false;
focusOpen = false;
schemaReady = false;
private schemaLoading = false;
@ -368,6 +370,13 @@ export class GenericNodeComponent implements OnDestroy {
return this.blockId ?? 'unknown-id';
}
toggleNodeSettings(event?: Event) {
event?.preventDefault();
event?.stopPropagation();
this.nodeSettingsOpen = !this.nodeSettingsOpen;
this.cdr.markForCheck();
}
toggleFocus(event?: Event) {
event?.preventDefault();
event?.stopPropagation();
@ -431,6 +440,7 @@ export class GenericNodeComponent implements OnDestroy {
this.localEditorGlobalInputs = [];
this.localEditorFreeText = false;
this.localEditorBindableInputName = null;
this.cdr.markForCheck();
}
async openParameterEditor(path: string, event?: Event) {

View File

@ -4,6 +4,7 @@
Attribution term under AGPL-3.0 section 7(b): see LICENSE-ADDENDUM.
-->
@if (showParameters) {
<div class="llm-params llm-params-shell">
@if (!host.schemaReady) {
<div class="llm-skeleton-overlay" aria-hidden="true">
@ -245,6 +246,7 @@
}
}
</div>
}
@if (host.localEditorOpen) {
<dialog #simpleEditorDialog class="llm-modal-backdrop" (pointerdown)="$event.stopPropagation()" (click)="onSimpleEditorDialogClick($event)" (cancel)="host.closeSimpleParamEditor($event)">

View File

@ -79,6 +79,8 @@ export type NodeParametersHost = {
})
export class NodeParametersComponent {
@Input({ required: true }) host!: NodeParametersHost;
/** The editor dialog must stay mounted even while the card's fields are collapsed. */
@Input() showParameters = true;
/**
* This renders inside a node card, which rete.js positions with a CSS `transform` for pan and

View File

@ -225,10 +225,13 @@
}
.resource-card__settings {
margin-top: 10px;
cursor: default;
}
.resource-card__settings--open {
margin-top: 10px;
}
.resource-card__confirm {
display: flex;
align-items: center;

View File

@ -90,11 +90,10 @@
</div>
}
}
} @else {
<div class="resource-card__settings" (pointerdown)="$event.stopPropagation()">
<app-node-parameters [host]="this" />
</div>
}
<div class="resource-card__settings" [class.resource-card__settings--open]="settingsOpen() || focusOpen" (pointerdown)="$event.stopPropagation()">
<app-node-parameters [host]="this" [showParameters]="settingsOpen() || focusOpen" />
</div>
@if (resourceLinkOutput; as resourceLinkOutput) {
<div class="resource-card__link" title="Drag to a storage operation to link it here">