fix: preserve expanded node mode on boolean toggles

This commit is contained in:
Lucio Lelii 2026-05-18 12:49:31 +02:00
parent 5a8d44d644
commit 0c64850b7d
6 changed files with 74 additions and 3 deletions

View File

@ -12,6 +12,7 @@ export type HFNodeData = FlowNode & {
__needsServerCreate?: boolean;
__createdOnServer?: boolean;
__isCreatingOnServer?: boolean;
__focusOpen?: boolean;
__updateBlockError?: string | null;
__containerValidationErrors?: unknown[];
__containerAssignmentError?: string | null;

View File

@ -57,6 +57,26 @@ describe('ContainerNodeComponent', () => {
expect(component.richContentFields.map((field) => field.path)).toContain('prompt');
});
it('restores and syncs persisted expanded-mode state', () => {
component.data = {
data: {
__focusOpen: true,
specificConfiguration: {},
inputs: [],
outputs: []
}
};
(component as any).restorePersistedFocusState();
expect(component.focusOpen).toBe(true);
expect(component.data.data.__focusOpen).toBe(true);
component.toggleFocus();
expect(component.focusOpen).toBe(false);
expect(component.data.data.__focusOpen).toBe(false);
});
it('loads retriever-backed options for nested LLM descriptor fields', async () => {
const schema = {
type: 'object',

View File

@ -131,6 +131,7 @@ export class ContainerNodeComponent implements OnDestroy {
}
ngOnInit() {
this.restorePersistedFocusState();
void this.loadSchemaContext();
}
@ -175,6 +176,7 @@ export class ContainerNodeComponent implements OnDestroy {
private setFocusOpen(value: boolean) {
if (this.focusOpen === value) return;
this.focusOpen = value;
this.syncPersistedFocusState();
if (value) {
this.attachHostToModalLayer();
this.applyPageScrollLock();
@ -185,6 +187,18 @@ export class ContainerNodeComponent implements OnDestroy {
this.cdr.markForCheck();
}
private restorePersistedFocusState() {
const nodeData = this.data?.data as Record<string, unknown> | undefined;
if (nodeData?.['__focusOpen'] !== true) return;
this.setFocusOpen(true);
}
private syncPersistedFocusState() {
const nodeData = this.data?.data as Record<string, unknown> | undefined;
if (!nodeData) return;
nodeData['__focusOpen'] = this.focusOpen;
}
private applyPageScrollLock() {
if (this.pageScrollLocked) return;
const body = document.body;
@ -1063,7 +1077,8 @@ export class ContainerNodeComponent implements OnDestroy {
if (typeof replaceNode === 'function') {
await replaceNode({
...createdContainer,
position: (current['position'] as { x: number; y: number } | undefined) ?? createdContainer.position
position: (current['position'] as { x: number; y: number } | undefined) ?? createdContainer.position,
__focusOpen: current['__focusOpen'] === true
});
return;
}

View File

@ -75,4 +75,18 @@ describe('GenericNodeComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
it('restores and syncs persisted expanded-mode state', () => {
const nodeData = component.data.data as Record<string, unknown>;
nodeData['__focusOpen'] = true;
(component as any).restorePersistedFocusState();
expect(component.focusOpen).toBe(true);
expect(nodeData['__focusOpen']).toBe(true);
component.toggleFocus();
expect(component.focusOpen).toBe(false);
expect(nodeData['__focusOpen']).toBe(false);
});
});

View File

@ -242,6 +242,7 @@ export class GenericNodeComponent implements OnDestroy {
this.refreshValidationState();
this.refreshParameterFields();
this.restorePersistedFocusState();
void this.loadSchemaContext();
}
@ -290,6 +291,7 @@ export class GenericNodeComponent implements OnDestroy {
private setFocusOpen(value: boolean) {
if (this.focusOpen === value) return;
this.focusOpen = value;
this.syncPersistedFocusState();
if (value) {
this.attachHostToModalLayer();
this.applyPageScrollLock();
@ -300,6 +302,18 @@ export class GenericNodeComponent implements OnDestroy {
this.cdr.markForCheck();
}
private restorePersistedFocusState() {
const nodeData = this.data?.data as Record<string, unknown> | undefined;
if (nodeData?.['__focusOpen'] !== true) return;
this.setFocusOpen(true);
}
private syncPersistedFocusState() {
const nodeData = this.data?.data as Record<string, unknown> | undefined;
if (!nodeData) return;
nodeData['__focusOpen'] = this.focusOpen;
}
private applyPageScrollLock() {
if (this.pageScrollLocked) return;
const body = document.body;
@ -1868,7 +1882,8 @@ export class GenericNodeComponent implements OnDestroy {
if (typeof replaceNode === 'function') {
void replaceNode({
...createdBlock,
position: (current['position'] as { x: number; y: number } | undefined) ?? createdBlock.position
position: (current['position'] as { x: number; y: number } | undefined) ?? createdBlock.position,
__focusOpen: current['__focusOpen'] === true
});
return;
}

View File

@ -409,6 +409,7 @@ export async function addBlockToEditor(
};
const replaceWithCreatedNode = async (createdBlock: FlowNode) => {
if (!editor.getNode(node.id)) return;
const createdBlockData = createdBlock as FlowNode & Record<string, unknown>;
const previousConnections = editor.getConnections()
.filter((connection) => connection.source === node.id || connection.target === node.id)
.map((connection) => ({
@ -422,7 +423,11 @@ export async function addBlockToEditor(
const replacementNode = await addBlockToEditor(
editor,
area,
{ ...createdBlock, position: currentPosition },
{
...createdBlockData,
position: currentPosition,
__focusOpen: node.data?.['__focusOpen'] === true || createdBlockData['__focusOpen'] === true
} as FlowNode & Record<string, unknown>,
currentPosition,
resolvedRuntime
);
@ -493,6 +498,7 @@ export async function addBlockToEditor(
__needsServerCreate: sourceData['nodeFamily'] === 'container' ? false : true,
__createdOnServer: false,
__isCreatingOnServer: false,
__focusOpen: false,
__updateBlockError: null
} as FlowNode & Record<string, unknown>;