mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(editor): Update default positions for AI sub-nodes (no-changelog) (#16870)
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
2770645eb4
commit
0ce3875f09
@@ -388,6 +388,144 @@ describe('useCanvasOperations', () => {
|
||||
|
||||
expect(position).toEqual([0, 0]);
|
||||
});
|
||||
|
||||
it('should apply custom Y offset for AI Language Model connection type', () => {
|
||||
const uiStore = mockedStore(useUIStore);
|
||||
const workflowsStore = mockedStore(useWorkflowsStore);
|
||||
const nodeTypesStore = mockedStore(useNodeTypesStore);
|
||||
|
||||
const node = createTestNode({ id: '0' });
|
||||
const nodeTypeDescription = mockNodeTypeDescription();
|
||||
const workflowObject = createTestWorkflowObject(workflowsStore.workflow);
|
||||
|
||||
uiStore.lastInteractedWithNode = createTestNode({
|
||||
position: [100, 100],
|
||||
type: 'test',
|
||||
typeVersion: 1,
|
||||
});
|
||||
uiStore.lastInteractedWithNodeHandle = `outputs/${NodeConnectionTypes.AiLanguageModel}/0`;
|
||||
nodeTypesStore.getNodeType = vi.fn().mockReturnValue(nodeTypeDescription);
|
||||
workflowsStore.getCurrentWorkflow.mockReturnValue(workflowObject);
|
||||
workflowObject.getNode = vi.fn().mockReturnValue(node);
|
||||
|
||||
vi.spyOn(NodeHelpers, 'getNodeOutputs').mockReturnValueOnce([
|
||||
{ type: NodeConnectionTypes.AiLanguageModel },
|
||||
]);
|
||||
vi.spyOn(NodeHelpers, 'getConnectionTypes')
|
||||
.mockReturnValueOnce([])
|
||||
.mockReturnValueOnce([])
|
||||
.mockReturnValueOnce([NodeConnectionTypes.AiLanguageModel]);
|
||||
|
||||
const { resolveNodePosition } = useCanvasOperations();
|
||||
const position = resolveNodePosition({ ...node, position: undefined }, nodeTypeDescription);
|
||||
|
||||
// Configuration node size is [200, 80], so customOffset = 200 * 2 = 400
|
||||
// Expected position: [100 + (200/1) * 1 - 200/2 - 400, 100 + 220] = [-200, 320]
|
||||
expect(position[0]).toBeLessThan(100); // Node should be moved left due to custom offset
|
||||
expect(position[1]).toEqual(320); // Standard Y position for configuration nodes
|
||||
});
|
||||
|
||||
it('should apply custom Y offset for AI Memory connection type', () => {
|
||||
const uiStore = mockedStore(useUIStore);
|
||||
const workflowsStore = mockedStore(useWorkflowsStore);
|
||||
const nodeTypesStore = mockedStore(useNodeTypesStore);
|
||||
|
||||
const node = createTestNode({ id: '0' });
|
||||
const nodeTypeDescription = mockNodeTypeDescription();
|
||||
const workflowObject = createTestWorkflowObject(workflowsStore.workflow);
|
||||
|
||||
uiStore.lastInteractedWithNode = createTestNode({
|
||||
position: [100, 100],
|
||||
type: 'test',
|
||||
typeVersion: 1,
|
||||
});
|
||||
uiStore.lastInteractedWithNodeHandle = `outputs/${NodeConnectionTypes.AiMemory}/0`;
|
||||
nodeTypesStore.getNodeType = vi.fn().mockReturnValue(nodeTypeDescription);
|
||||
workflowsStore.getCurrentWorkflow.mockReturnValue(workflowObject);
|
||||
workflowObject.getNode = vi.fn().mockReturnValue(node);
|
||||
|
||||
vi.spyOn(NodeHelpers, 'getNodeOutputs').mockReturnValueOnce([
|
||||
{ type: NodeConnectionTypes.AiMemory },
|
||||
]);
|
||||
vi.spyOn(NodeHelpers, 'getConnectionTypes')
|
||||
.mockReturnValueOnce([])
|
||||
.mockReturnValueOnce([])
|
||||
.mockReturnValueOnce([NodeConnectionTypes.AiMemory]);
|
||||
|
||||
const { resolveNodePosition } = useCanvasOperations();
|
||||
const position = resolveNodePosition({ ...node, position: undefined }, nodeTypeDescription);
|
||||
|
||||
expect(position[0]).toBeLessThan(100); // Node should be moved left due to custom offset
|
||||
expect(position[1]).toEqual(320); // Standard Y position for configuration nodes
|
||||
});
|
||||
|
||||
it('should not apply custom offset for regular connection types', () => {
|
||||
const uiStore = mockedStore(useUIStore);
|
||||
const workflowsStore = mockedStore(useWorkflowsStore);
|
||||
const nodeTypesStore = mockedStore(useNodeTypesStore);
|
||||
|
||||
const node = createTestNode({ id: '0' });
|
||||
const nodeTypeDescription = mockNodeTypeDescription();
|
||||
const workflowObject = createTestWorkflowObject(workflowsStore.workflow);
|
||||
|
||||
uiStore.lastInteractedWithNode = createTestNode({
|
||||
position: [100, 100],
|
||||
type: 'test',
|
||||
typeVersion: 1,
|
||||
});
|
||||
uiStore.lastInteractedWithNodeHandle = `outputs/${NodeConnectionTypes.AiTool}/0`;
|
||||
nodeTypesStore.getNodeType = vi.fn().mockReturnValue(nodeTypeDescription);
|
||||
workflowsStore.getCurrentWorkflow.mockReturnValue(workflowObject);
|
||||
workflowObject.getNode = vi.fn().mockReturnValue(node);
|
||||
|
||||
vi.spyOn(NodeHelpers, 'getNodeOutputs').mockReturnValueOnce([
|
||||
{ type: NodeConnectionTypes.AiTool },
|
||||
]);
|
||||
vi.spyOn(NodeHelpers, 'getConnectionTypes')
|
||||
.mockReturnValueOnce([])
|
||||
.mockReturnValueOnce([])
|
||||
.mockReturnValueOnce([NodeConnectionTypes.AiTool]);
|
||||
|
||||
const { resolveNodePosition } = useCanvasOperations();
|
||||
const position = resolveNodePosition({ ...node, position: undefined }, nodeTypeDescription);
|
||||
|
||||
// No custom offset applied
|
||||
expect(position).toEqual([60, 320]);
|
||||
});
|
||||
|
||||
it('should handle missing connection type gracefully', () => {
|
||||
const uiStore = mockedStore(useUIStore);
|
||||
const workflowsStore = mockedStore(useWorkflowsStore);
|
||||
const nodeTypesStore = mockedStore(useNodeTypesStore);
|
||||
|
||||
const node = createTestNode({ id: '0' });
|
||||
const nodeTypeDescription = mockNodeTypeDescription();
|
||||
const workflowObject = createTestWorkflowObject(workflowsStore.workflow);
|
||||
|
||||
uiStore.lastInteractedWithNode = createTestNode({
|
||||
position: [100, 100],
|
||||
type: 'test',
|
||||
typeVersion: 1,
|
||||
});
|
||||
// No lastInteractedWithNodeHandle set
|
||||
nodeTypesStore.getNodeType = vi.fn().mockReturnValue(nodeTypeDescription);
|
||||
workflowsStore.getCurrentWorkflow.mockReturnValue(workflowObject);
|
||||
workflowObject.getNode = vi.fn().mockReturnValue(node);
|
||||
|
||||
vi.spyOn(NodeHelpers, 'getNodeOutputs').mockReturnValueOnce([
|
||||
{ type: NodeConnectionTypes.AiTool },
|
||||
]);
|
||||
vi.spyOn(NodeHelpers, 'getConnectionTypes')
|
||||
.mockReturnValueOnce([])
|
||||
.mockReturnValueOnce([])
|
||||
.mockReturnValueOnce([NodeConnectionTypes.AiTool]);
|
||||
|
||||
const { resolveNodePosition } = useCanvasOperations();
|
||||
const position = resolveNodePosition({ ...node, position: undefined }, nodeTypeDescription);
|
||||
|
||||
// No custom offset applied
|
||||
expect(position).toEqual([60, 320]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateNodesPosition', () => {
|
||||
|
||||
@@ -1029,7 +1029,7 @@ export function useCanvasOperations() {
|
||||
|
||||
const nodeSize =
|
||||
connectionType === NodeConnectionTypes.Main ? DEFAULT_NODE_SIZE : CONFIGURATION_NODE_SIZE;
|
||||
let pushOffsets: XYPosition = [nodeSize[0] / 2, nodeSize[1] / 2];
|
||||
const pushOffsets: XYPosition = [nodeSize[0] / 2, nodeSize[1] / 2];
|
||||
|
||||
let position: XYPosition | undefined = node.position;
|
||||
if (position) {
|
||||
@@ -1130,7 +1130,17 @@ export function useCanvasOperations() {
|
||||
} catch (e) {}
|
||||
const outputTypes = NodeHelpers.getConnectionTypes(outputs);
|
||||
|
||||
pushOffsets = [100, 0];
|
||||
/**
|
||||
* Custom Y offsets for specific connection types when adding them using the plus button:
|
||||
* - AI Language Model: Moved left by 2 node widths
|
||||
* - AI Memory: Moved left by 1 node width
|
||||
*/
|
||||
const CUSTOM_Y_OFFSETS: Record<string, number> = {
|
||||
[NodeConnectionTypes.AiLanguageModel]: nodeSize[0] * 2,
|
||||
[NodeConnectionTypes.AiMemory]: nodeSize[0],
|
||||
};
|
||||
|
||||
const customOffset: number = CUSTOM_Y_OFFSETS[connectionType as string] ?? 0;
|
||||
|
||||
if (
|
||||
outputTypes.length > 0 &&
|
||||
@@ -1152,7 +1162,8 @@ export function useCanvasOperations() {
|
||||
lastInteractedWithNode.position[0] +
|
||||
(CONFIGURABLE_NODE_SIZE[0] / lastInteractedWithNodeWidthDivisions) *
|
||||
(scopedConnectionIndex + 1) -
|
||||
nodeSize[0] / 2,
|
||||
nodeSize[0] / 2 -
|
||||
customOffset,
|
||||
lastInteractedWithNode.position[1] + PUSH_NODES_OFFSET,
|
||||
];
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user